nir/inline_functions: Don't shadown variables when it isn't needed
authorJason Ekstrand <jason.ekstrand@intel.com>
Tue, 9 Feb 2016 01:41:36 +0000 (17:41 -0800)
committerJason Ekstrand <jason.ekstrand@intel.com>
Tue, 9 Feb 2016 18:29:05 +0000 (10:29 -0800)
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

index b343eb735b19f3819fb270f6dd1874a475b6b3ea..4a08dcc96e0cab90df10a8c7b1be2fd0e66683ef 100644 (file)
@@ -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, &param->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, &param->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));