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));