glsl: Generate correct ir_binop_vector_extract code for out and inout parameters
authorIan Romanick <ian.d.romanick@intel.com>
Sat, 23 Mar 2013 00:45:52 +0000 (17:45 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Mon, 13 May 2013 19:05:19 +0000 (12:05 -0700)
Like with type conversions on out parameters, some extra copies need to
occur to handle these cases.  The fundamental problem is that
ir_binop_vector_extract is not an lvalue, but out and inout parameters
must be lvalues.  A previous patch delt with a similar problem in the
LHS of ir_assignment.

v2: Convert tabs to spaces.  Suggested by Eric.

Signed-off-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Kenneth Graunke <kenneth@whitecape.org>
src/glsl/ast_function.cpp

index 26f72cf8e95ad343e8e0b772b467181087b62859..00e0c05dddc0523088edc05935cec83c444fb0aa 100644 (file)
@@ -165,10 +165,18 @@ verify_parameter_modes(_mesa_glsl_parse_state *state,
                             actual->variable_referenced()->name);
            return false;
         } else if (!actual->is_lvalue()) {
-           _mesa_glsl_error(&loc, state,
-                            "function parameter '%s %s' is not an lvalue",
-                            mode, formal->name);
-           return false;
+            /* Even though ir_binop_vector_extract is not an l-value, let it
+             * slop through.  generate_call will handle it correctly.
+             */
+            ir_expression *const expr = ((ir_rvalue *) actual)->as_expression();
+            if (expr == NULL
+                || expr->operation != ir_binop_vector_extract
+                || !expr->operands[0]->is_lvalue()) {
+               _mesa_glsl_error(&loc, state,
+                                "function parameter '%s %s' is not an lvalue",
+                                mode, formal->name);
+               return false;
+            }
         }
       }
 
@@ -178,6 +186,93 @@ verify_parameter_modes(_mesa_glsl_parse_state *state,
    return true;
 }
 
+static void
+fix_parameter(void *mem_ctx, ir_rvalue *actual, const glsl_type *formal_type,
+              exec_list *before_instructions, exec_list *after_instructions,
+              bool parameter_is_inout)
+{
+   ir_expression *const expr = actual->as_expression();
+
+   /* If the types match exactly and the parameter is not a vector-extract,
+    * nothing needs to be done to fix the parameter.
+    */
+   if (formal_type == actual->type
+       && (expr == NULL || expr->operation != ir_binop_vector_extract))
+      return;
+
+   /* To convert an out parameter, we need to create a temporary variable to
+    * hold the value before conversion, and then perform the conversion after
+    * the function call returns.
+    *
+    * This has the effect of transforming code like this:
+    *
+    *   void f(out int x);
+    *   float value;
+    *   f(value);
+    *
+    * Into IR that's equivalent to this:
+    *
+    *   void f(out int x);
+    *   float value;
+    *   int out_parameter_conversion;
+    *   f(out_parameter_conversion);
+    *   value = float(out_parameter_conversion);
+    *
+    * If the parameter is an ir_expression of ir_binop_vector_extract,
+    * additional conversion is needed in the post-call re-write.
+    */
+   ir_variable *tmp =
+      new(mem_ctx) ir_variable(formal_type, "inout_tmp", ir_var_temporary);
+
+   before_instructions->push_tail(tmp);
+
+   /* If the parameter is an inout parameter, copy the value of the actual
+    * parameter to the new temporary.  Note that no type conversion is allowed
+    * here because inout parameters must match types exactly.
+    */
+   if (parameter_is_inout) {
+      /* Inout parameters should never require conversion, since that would
+       * require an implicit conversion to exist both to and from the formal
+       * parameter type, and there are no bidirectional implicit conversions.
+       */
+      assert (actual->type == formal_type);
+
+      ir_dereference_variable *const deref_tmp_1 =
+         new(mem_ctx) ir_dereference_variable(tmp);
+      ir_assignment *const assignment =
+         new(mem_ctx) ir_assignment(deref_tmp_1, actual);
+      before_instructions->push_tail(assignment);
+   }
+
+   /* Replace the parameter in the call with a dereference of the new
+    * temporary.
+    */
+   ir_dereference_variable *const deref_tmp_2 =
+      new(mem_ctx) ir_dereference_variable(tmp);
+   actual->replace_with(deref_tmp_2);
+
+
+   /* Copy the temporary variable to the actual parameter with optional
+    * type conversion applied.
+    */
+   ir_rvalue *rhs = new(mem_ctx) ir_dereference_variable(tmp);
+   if (actual->type != formal_type)
+      rhs = convert_component(rhs, actual->type);
+
+   ir_rvalue *lhs = actual;
+   if (expr != NULL && expr->operation == ir_binop_vector_extract) {
+      rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert,
+                                       expr->operands[0]->type,
+                                       expr->operands[0]->clone(mem_ctx, NULL),
+                                       rhs,
+                                       expr->operands[1]->clone(mem_ctx, NULL));
+      lhs = expr->operands[0]->clone(mem_ctx, NULL);
+   }
+
+   ir_assignment *const assignment_2 = new(mem_ctx) ir_assignment(lhs, rhs);
+   after_instructions->push_tail(assignment_2);
+}
+
 /**
  * If a function call is generated, \c call_ir will point to it on exit.
  * Otherwise \c call_ir will be set to \c NULL.
@@ -218,50 +313,10 @@ generate_call(exec_list *instructions, ir_function_signature *sig,
            break;
         }
         case ir_var_function_out:
-           if (actual->type != formal->type) {
-              /* To convert an out parameter, we need to create a
-               * temporary variable to hold the value before conversion,
-               * and then perform the conversion after the function call
-               * returns.
-               *
-               * This has the effect of transforming code like this:
-               *
-               *   void f(out int x);
-               *   float value;
-               *   f(value);
-               *
-               * Into IR that's equivalent to this:
-               *
-               *   void f(out int x);
-               *   float value;
-               *   int out_parameter_conversion;
-               *   f(out_parameter_conversion);
-               *   value = float(out_parameter_conversion);
-               */
-              ir_variable *tmp =
-                 new(ctx) ir_variable(formal->type,
-                                      "out_parameter_conversion",
-                                      ir_var_temporary);
-              instructions->push_tail(tmp);
-              ir_dereference_variable *deref_tmp_1
-                 = new(ctx) ir_dereference_variable(tmp);
-              ir_dereference_variable *deref_tmp_2
-                 = new(ctx) ir_dereference_variable(tmp);
-              ir_rvalue *converted_tmp
-                 = convert_component(deref_tmp_1, actual->type);
-              ir_assignment *assignment
-                 = new(ctx) ir_assignment(actual, converted_tmp);
-              post_call_conversions.push_tail(assignment);
-              actual->replace_with(deref_tmp_2);
-           }
-           break;
         case ir_var_function_inout:
-           /* Inout parameters should never require conversion, since that
-            * would require an implicit conversion to exist both to and
-            * from the formal parameter type, and there are no
-            * bidirectional implicit conversions.
-            */
-           assert (actual->type == formal->type);
+            fix_parameter(ctx, actual, formal->type,
+                          instructions, &post_call_conversions,
+                          formal->mode == ir_var_function_inout);
            break;
         default:
            assert (!"Illegal formal parameter mode");