From de6c9c5f2e1e975d06e4d27cefc6af9ae53d75a7 Mon Sep 17 00:00:00 2001 From: Jason Ekstrand Date: Mon, 8 Feb 2016 17:41:36 -0800 Subject: [PATCH] nir/inline_functions: Don't shadown variables when it isn't needed Previously, in order to get things working, we just always shadowed variables. Now, we rewrite derefs whenever it's safe to do so and only shadow if we have an in or out variable that we write or read to respectively. --- src/compiler/nir/nir_inline_functions.c | 154 ++++++++++++++++++++---- 1 file changed, 131 insertions(+), 23 deletions(-) diff --git a/src/compiler/nir/nir_inline_functions.c b/src/compiler/nir/nir_inline_functions.c index b343eb735b1..4a08dcc96e0 100644 --- a/src/compiler/nir/nir_inline_functions.c +++ b/src/compiler/nir/nir_inline_functions.c @@ -33,6 +33,106 @@ struct inline_functions_state { static bool inline_function_impl(nir_function_impl *impl, struct set *inlined); +static bool +rewrite_param_derefs_block(nir_block *block, void *void_state) +{ + nir_call_instr *call = void_state; + + nir_foreach_instr_safe(block, instr) { + if (instr->type != nir_instr_type_intrinsic) + continue; + + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); + + for (unsigned i = 0; + i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) { + if (intrin->variables[i]->var->data.mode != nir_var_param) + continue; + + int param_idx = intrin->variables[i]->var->data.location; + + nir_deref_var *call_deref; + if (param_idx >= 0) { + assert(param_idx < call->callee->num_params); + call_deref = call->params[param_idx]; + } else { + call_deref = call->return_deref; + } + assert(call_deref); + + nir_deref_var *new_deref = nir_deref_as_var(nir_copy_deref(intrin, &call_deref->deref)); + nir_deref *new_tail = nir_deref_tail(&new_deref->deref); + new_tail->child = intrin->variables[i]->deref.child; + ralloc_steal(new_tail, new_tail->child); + intrin->variables[i] = new_deref; + } + } + + return true; +} + +static void +lower_param_to_local(nir_variable *param, nir_function_impl *impl, bool write) +{ + if (param->data.mode != nir_var_param) + return; + + nir_parameter_type param_type; + if (param->data.location >= 0) { + assert(param->data.location < impl->num_params); + param_type = impl->function->params[param->data.location].param_type; + } else { + /* Return variable */ + param_type = nir_parameter_out; + } + + if ((write && param_type == nir_parameter_in) || + (!write && param_type == nir_parameter_out)) { + /* In this case, we need a shadow copy. Turn it into a local */ + param->data.mode = nir_var_local; + exec_list_push_tail(&impl->locals, ¶m->node); + } +} + +static bool +lower_params_to_locals_block(nir_block *block, void *void_state) +{ + nir_function_impl *impl = void_state; + + nir_foreach_instr_safe(block, instr) { + if (instr->type != nir_instr_type_intrinsic) + continue; + + nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr); + + switch (intrin->intrinsic) { + case nir_intrinsic_store_var: + lower_param_to_local(intrin->variables[0]->var, impl, true); + break; + + case nir_intrinsic_copy_var: + lower_param_to_local(intrin->variables[0]->var, impl, true); + lower_param_to_local(intrin->variables[1]->var, impl, false); + break; + + case nir_intrinsic_load_var: + /* All other intrinsics which access variables (image_load_store) + * do so in a read-only fasion. + */ + for (unsigned i = 0; + i < nir_intrinsic_infos[intrin->intrinsic].num_variables; i++) { + lower_param_to_local(intrin->variables[i]->var, impl, false); + } + break; + + default: + continue; + } + } + + return true; +} + static bool inline_functions_block(nir_block *block, void *void_state) { @@ -60,29 +160,40 @@ inline_functions_block(nir_block *block, void *void_state) nir_function_impl *callee_copy = nir_function_impl_clone(call->callee->impl); + callee_copy->function = call->callee; + + /* Add copies of all in parameters */ + assert(call->num_params == callee_copy->num_params); exec_list_append(&b->impl->locals, &callee_copy->locals); exec_list_append(&b->impl->registers, &callee_copy->registers); b->cursor = nir_before_instr(&call->instr); - /* Add copies of all in parameters */ - assert(call->num_params == callee_copy->num_params); + /* We now need to tie the two functions together using the + * parameters. There are two ways we do this: One is to turn the + * parameter into a local variable and do a shadow-copy. The other + * is to treat the parameter as a "proxy" and rewrite derefs to use + * the actual variable that comes from the call instruction. We + * implement both schemes. The first is needed in the case where we + * have an in parameter that we write or similar. The second case is + * needed for handling things such as images and uniforms properly. + */ + + /* Figure out when we need to lower to a shadow local */ + nir_foreach_block(callee_copy, lower_params_to_locals_block, callee_copy); for (unsigned i = 0; i < callee_copy->num_params; i++) { nir_variable *param = callee_copy->params[i]; - /* Turn it into a local variable */ - param->data.mode = nir_var_local; - exec_list_push_head(&b->impl->locals, ¶m->node); - - /* Only in or inout parameters */ - if (call->callee->params[i].param_type == nir_parameter_out) - continue; - - nir_copy_deref_var(b, nir_deref_var_create(b->shader, param), - call->params[i]); + if (param->data.mode == nir_var_local && + call->callee->params[i].param_type != nir_parameter_out) { + nir_copy_deref_var(b, nir_deref_var_create(b->shader, param), + call->params[i]); + } } + nir_foreach_block(callee_copy, rewrite_param_derefs_block, call); + /* Pluck the body out of the function and place it here */ nir_cf_list body; nir_cf_list_extract(&body, &callee_copy->body); @@ -93,19 +204,16 @@ inline_functions_block(nir_block *block, void *void_state) /* Add copies of all out parameters and the return */ assert(call->num_params == callee_copy->num_params); for (unsigned i = 0; i < callee_copy->num_params; i++) { - /* Only out or inout parameters */ - if (call->callee->params[i].param_type == nir_parameter_in) - continue; + nir_variable *param = callee_copy->params[i]; - nir_copy_deref_var(b, call->params[i], - nir_deref_var_create(b->shader, - callee_copy->params[i])); + if (param->data.mode == nir_var_local && + call->callee->params[i].param_type != nir_parameter_in) { + nir_copy_deref_var(b, call->params[i], + nir_deref_var_create(b->shader, param)); + } } - if (!glsl_type_is_void(call->callee->return_type)) { - /* Turn it into a local variable */ - callee_copy->return_var->data.mode = nir_var_local; - exec_list_push_head(&b->impl->locals, &callee_copy->return_var->node); - + if (!glsl_type_is_void(call->callee->return_type) && + callee_copy->return_var->data.mode == nir_var_local) { nir_copy_deref_var(b, call->return_deref, nir_deref_var_create(b->shader, callee_copy->return_var)); -- 2.30.2