[Mesa-dev] [PATCH 3/3] glsl: Perform implicit type conversions on function call out parameters.

Paul Berry stereotype441 at gmail.com
Tue Aug 2 17:38:41 PDT 2011


When an out parameter undergoes an implicit type conversion, we need
to store it in a temporary, and then after the call completes, convert
the resulting value.  In other words, we convert code like the
following:

void f(out int x);
float value;
f(value);

Into IR that's equivalent to this:

void f(out int x);
float value;
int x_converted;
f(x_converted);
value = float(x_converted);

This transformation needs to happen during ast-to-IR convertion (as
opposed to, say, a lowering pass), because it is invalid IR for formal
and actual parameters to have types that don't match.

Fixes piglit tests spec/glsl-1.20/compiler/qualifiers/out-03.vert and
spec/glsl-1.20/execution/qualifiers/vs-out-{01,02,03}.shader_test, and
bug 39651.

Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=39651
---
 src/glsl/ast_function.cpp |   64 +++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 59 insertions(+), 5 deletions(-)

diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp
index 8bcf48d..f945a94 100644
--- a/src/glsl/ast_function.cpp
+++ b/src/glsl/ast_function.cpp
@@ -134,6 +134,8 @@ match_function_by_name(exec_list *instructions, const char *name,
       }
    }
 
+   exec_list post_call_conversions;
+
    if (sig != NULL) {
       /* Verify that 'out' and 'inout' actual parameters are lvalues.  This
        * isn't done in ir_function::matching_signature because that function
@@ -141,6 +143,13 @@ match_function_by_name(exec_list *instructions, const char *name,
        *
        * Also, validate that 'const_in' formal parameters (an extension of our
        * IR) correspond to ir_constant actual parameters.
+       *
+       * Also, perform implicit conversion of arguments.  Note: to
+       * implicitly convert out parameters, we need to place them in a
+       * temporary variable, and do the conversion after the call
+       * takes place.  Since we haven't emitted the call yet, we'll
+       * place the post-call conversions in a temporary exec_list, and
+       * emit them later.
        */
       exec_list_iterator actual_iter = actual_parameters->iterator();
       exec_list_iterator formal_iter = sig->parameters.iterator();
@@ -185,8 +194,50 @@ match_function_by_name(exec_list *instructions, const char *name,
 	 }
 
 	 if (formal->type->is_numeric() || formal->type->is_boolean()) {
-	    ir_rvalue *converted = convert_component(actual, formal->type);
-	    actual->replace_with(converted);
+            switch (formal->mode) {
+            case ir_var_in:
+               {
+                  ir_rvalue *converted
+                     = convert_component(actual, formal->type);
+                  actual->replace_with(converted);
+               }
+               break;
+            case ir_var_out:
+               if (actual->type != formal->type)
+               {
+                  /* Create a temporary variable to hold the out
+                   * parameter.  Don't pass a hashtable to clone()
+                   * because we don't want this temporary variable to
+                   * be in scope.
+                   */
+                  ir_variable *tmp = formal->clone(ctx, NULL);
+                  tmp->mode = ir_var_auto;
+                  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_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);
+               break;
+            default:
+               assert (!"Illegal formal parameter mode");
+               break;
+            }
 	 }
 
 	 actual_iter.next();
@@ -196,11 +247,13 @@ match_function_by_name(exec_list *instructions, const char *name,
       /* Always insert the call in the instruction stream, and return a deref
        * of its return val if it returns a value, since we don't know if
        * the rvalue is going to be assigned to anything or not.
+       *
+       * Also insert any out parameter conversions after the call.
        */
       ir_call *call = new(ctx) ir_call(sig, actual_parameters);
+      ir_dereference_variable *deref;
       if (!sig->return_type->is_void()) {
 	 ir_variable *var;
-	 ir_dereference_variable *deref;
 
 	 var = new(ctx) ir_variable(sig->return_type,
 				    ralloc_asprintf(ctx, "%s_retval",
@@ -215,11 +268,12 @@ match_function_by_name(exec_list *instructions, const char *name,
 	    var->constant_value = call->constant_expression_value();
 
 	 deref = new(ctx) ir_dereference_variable(var);
-	 return deref;
       } else {
 	 instructions->push_tail(call);
-	 return NULL;
+	 deref = NULL;
       }
+      instructions->append_list(&post_call_conversions);
+      return deref;
    } else {
       char *str = prototype_string(NULL, name, actual_parameters);
 
-- 
1.7.6



More information about the mesa-dev mailing list