glsl: Split code to generate an ir_call out from match_function_by_name.
authorKenneth Graunke <kenneth@whitecape.org>
Wed, 9 Nov 2011 11:01:54 +0000 (03:01 -0800)
committerKenneth Graunke <kenneth@whitecape.org>
Tue, 15 Nov 2011 01:18:03 +0000 (17:18 -0800)
match_function_by_name performs two fairly separate tasks:
1. Hunt down the appropriate ir_function_signature for the callee.
2. Generate the actual ir_call (assuming we found the callee).

Both of these are complicated.  The first has to handle exact/inexact
matches, lazy importing of built-in prototypes, different scoping rules
for 1.10, 1.20+, and ES.  Not to mention printing a user-friendly error
message with pretty-printed "maybe you meant this" candidate signatures.

The second has to deal with void/non-void functions, pre-call implicit
conversions for "in" parmeters, and post-call "out" call conversions.

Trying to do both in one function is just too unwieldy.  Time to split.

This patch purely moves the code to generate an ir_call into a separate
function and reindents it.  Otherwise, the code is identical.

Signed-off-by: Kenneth Graunke <kenneth@whitecape.org>
Reviewed-by: Paul Berry <stereotype441@gmail.com>
src/glsl/ast_function.cpp

index fc0d7497d5383cedb0768d732d4345fd5108661c..08fdd64be56d5396280acb7ec68158dc9c094307 100644 (file)
@@ -93,6 +93,178 @@ prototype_string(const glsl_type *return_type, const char *name,
    return str;
 }
 
+static ir_rvalue *
+generate_call(exec_list *instructions, ir_function_signature *sig,
+             YYLTYPE *loc, exec_list *actual_parameters,
+             struct _mesa_glsl_parse_state *state)
+{
+   void *ctx = state;
+   exec_list post_call_conversions;
+
+   /* Verify that 'out' and 'inout' actual parameters are lvalues.  This
+    * isn't done in ir_function::matching_signature because that function
+    * cannot generate the necessary diagnostics.
+    *
+    * 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();
+
+   while (actual_iter.has_next()) {
+      ir_rvalue *actual = (ir_rvalue *) actual_iter.get();
+      ir_variable *formal = (ir_variable *) formal_iter.get();
+
+      assert(actual != NULL);
+      assert(formal != NULL);
+
+      if (formal->mode == ir_var_const_in && !actual->as_constant()) {
+        _mesa_glsl_error(loc, state,
+                         "parameter `%s' must be a constant expression",
+                         formal->name);
+        return ir_call::get_error_instruction(ctx);
+      }
+
+      if ((formal->mode == ir_var_out)
+         || (formal->mode == ir_var_inout)) {
+        const char *mode = NULL;
+        switch (formal->mode) {
+        case ir_var_out:   mode = "out";   break;
+        case ir_var_inout: mode = "inout"; break;
+        default:           assert(false);  break;
+        }
+        /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always
+         * FIXME: 0:0(0).
+         */
+        if (actual->variable_referenced()
+            && actual->variable_referenced()->read_only) {
+           _mesa_glsl_error(loc, state,
+                            "function parameter '%s %s' references the "
+                            "read-only variable '%s'",
+                            mode, formal->name,
+                            actual->variable_referenced()->name);
+
+        } else if (!actual->is_lvalue()) {
+           _mesa_glsl_error(loc, state,
+                            "function parameter '%s %s' is not an lvalue",
+                            mode, formal->name);
+        }
+      }
+
+      if (formal->type->is_numeric() || formal->type->is_boolean()) {
+        switch (formal->mode) {
+        case ir_var_const_in:
+        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) {
+              /* 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_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();
+      formal_iter.next();
+   }
+
+   /* 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()) {
+      /* If the function call is a constant expression, don't
+       * generate the instructions to call it; just generate an
+       * ir_constant representing the constant value.
+       *
+       * Function calls can only be constant expressions starting
+       * in GLSL 1.20.
+       */
+      if (state->language_version >= 120) {
+        ir_constant *const_val = call->constant_expression_value();
+        if (const_val) {
+           return const_val;
+        }
+      }
+
+      ir_variable *var;
+
+      var = new(ctx) ir_variable(sig->return_type,
+                                ralloc_asprintf(ctx, "%s_retval",
+                                                sig->function_name()),
+                                ir_var_temporary);
+      instructions->push_tail(var);
+
+      deref = new(ctx) ir_dereference_variable(var);
+      ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
+      instructions->push_tail(assign);
+
+      deref = new(ctx) ir_dereference_variable(var);
+   } else {
+      instructions->push_tail(call);
+      deref = NULL;
+   }
+   instructions->append_list(&post_call_conversions);
+   return deref;
+}
 
 static ir_rvalue *
 match_function_by_name(exec_list *instructions, const char *name,
@@ -135,172 +307,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
-       * cannot generate the necessary diagnostics.
-       *
-       * 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();
-
-      while (actual_iter.has_next()) {
-        ir_rvalue *actual = (ir_rvalue *) actual_iter.get();
-        ir_variable *formal = (ir_variable *) formal_iter.get();
-
-        assert(actual != NULL);
-        assert(formal != NULL);
-
-        if (formal->mode == ir_var_const_in && !actual->as_constant()) {
-           _mesa_glsl_error(loc, state,
-                            "parameter `%s' must be a constant expression",
-                            formal->name);
-           return ir_call::get_error_instruction(ctx);
-        }
-
-        if ((formal->mode == ir_var_out)
-            || (formal->mode == ir_var_inout)) {
-           const char *mode = NULL;
-           switch (formal->mode) {
-           case ir_var_out:   mode = "out";   break;
-           case ir_var_inout: mode = "inout"; break;
-           default:           assert(false);  break;
-           }
-            /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always
-             * FIXME: 0:0(0).
-             */
-           if (actual->variable_referenced()
-               && actual->variable_referenced()->read_only) {
-              _mesa_glsl_error(loc, state,
-                               "function parameter '%s %s' references the "
-                               "read-only variable '%s'",
-                               mode, formal->name,
-                               actual->variable_referenced()->name);
-
-           } else if (!actual->is_lvalue()) {
-               _mesa_glsl_error(loc, state,
-                                "function parameter '%s %s' is not an lvalue",
-                                mode, formal->name);
-           }
-        }
-
-        if (formal->type->is_numeric() || formal->type->is_boolean()) {
-            switch (formal->mode) {
-            case ir_var_const_in:
-            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) {
-                  /* 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_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();
-        formal_iter.next();
-      }
-
-      /* 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()) {
-         /* If the function call is a constant expression, don't
-          * generate the instructions to call it; just generate an
-          * ir_constant representing the constant value.
-          *
-          * Function calls can only be constant expressions starting
-          * in GLSL 1.20.
-          */
-         if (state->language_version >= 120) {
-            ir_constant *const_val = call->constant_expression_value();
-            if (const_val) {
-               return const_val;
-            }
-         }
-
-        ir_variable *var;
-
-        var = new(ctx) ir_variable(sig->return_type,
-                                   ralloc_asprintf(ctx, "%s_retval",
-                                                   sig->function_name()),
-                                   ir_var_temporary);
-        instructions->push_tail(var);
-
-        deref = new(ctx) ir_dereference_variable(var);
-        ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL);
-        instructions->push_tail(assign);
-
-        deref = new(ctx) ir_dereference_variable(var);
-      } else {
-        instructions->push_tail(call);
-        deref = NULL;
-      }
-      instructions->append_list(&post_call_conversions);
-      return deref;
+      return generate_call(instructions, sig, loc, actual_parameters, state);
    } else {
       char *str = prototype_string(NULL, name, actual_parameters);