ir_function_inlining: Re-add the "s/return/retval =/" functionality.
[mesa.git] / ast_function.cpp
index 6de72ab2d8a57a16530cf0daf25319022890ee19..ff2dfa502f3c6044d3337fe577cf70a9b4c2927b 100644 (file)
@@ -21,7 +21,6 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include <cstdio>
 #include "glsl_symbol_table.h"
 #include "ast.h"
 #include "glsl_types.h"
@@ -36,7 +35,11 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters,
 
    foreach_list (n, parameters) {
       ast_node *const ast = exec_node_data(ast_node, n, link);
-      ir_rvalue *const result = ast->hir(instructions, state);
+      ir_rvalue *result = ast->hir(instructions, state);
+
+      ir_constant *const constant = result->constant_expression_value();
+      if (constant != NULL)
+        result = constant;
 
       actual_parameters->push_tail(result);
       count++;
@@ -106,7 +109,7 @@ process_call(exec_list *instructions, ir_function *f,
 
 static ir_rvalue *
 match_function_by_name(exec_list *instructions, const char *name,
-                      YYLTYPE *loc, exec_list *parameters,
+                      YYLTYPE *loc, exec_list *actual_parameters,
                       struct _mesa_glsl_parse_state *state)
 {
    ir_function *f = state->symbols->get_function(name);
@@ -116,16 +119,10 @@ match_function_by_name(exec_list *instructions, const char *name,
       return ir_call::get_error_instruction();
    }
 
-   /* Once we've determined that the function being called might exist,
-    * process the parameters.
-    */
-   exec_list actual_parameters;
-   process_parameters(instructions, &actual_parameters, parameters, state);
-
-   /* After processing the function's actual parameters, try to find an
-    * overload of the function that matches.
+   /* Once we've determined that the function being called might exist, try
+    * to find an overload of the function that matches the parameters.
     */
-   return process_call(instructions, f, loc, &actual_parameters, state);
+   return process_call(instructions, f, loc, actual_parameters, state);
 }
 
 
@@ -137,6 +134,7 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type)
 {
    const unsigned a = desired_type->base_type;
    const unsigned b = src->type->base_type;
+   ir_expression *result = NULL;
 
    if (src->type->is_error())
       return src;
@@ -151,32 +149,42 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type)
    case GLSL_TYPE_UINT:
    case GLSL_TYPE_INT:
       if (b == GLSL_TYPE_FLOAT)
-        return new ir_expression(ir_unop_f2i, desired_type, src, NULL);
+        result = new ir_expression(ir_unop_f2i, desired_type, src, NULL);
       else {
         assert(b == GLSL_TYPE_BOOL);
-        return new ir_expression(ir_unop_b2i, desired_type, src, NULL);
+        result = new ir_expression(ir_unop_b2i, desired_type, src, NULL);
       }
       break;
    case GLSL_TYPE_FLOAT:
       switch (b) {
       case GLSL_TYPE_UINT:
-        return new ir_expression(ir_unop_u2f, desired_type, src, NULL);
+        result = new ir_expression(ir_unop_u2f, desired_type, src, NULL);
+        break;
       case GLSL_TYPE_INT:
-        return new ir_expression(ir_unop_i2f, desired_type, src, NULL);
+        result = new ir_expression(ir_unop_i2f, desired_type, src, NULL);
+        break;
       case GLSL_TYPE_BOOL:
-        return new ir_expression(ir_unop_b2f, desired_type, src, NULL);
+        result = new ir_expression(ir_unop_b2f, desired_type, src, NULL);
+        break;
       }
       break;
    case GLSL_TYPE_BOOL: {
-      int z = 0;
-      ir_constant *const zero = new ir_constant(src->type, &z);
+      ir_constant *zero = NULL;
+
+      switch (b) {
+      case GLSL_TYPE_UINT:  zero = new ir_constant(unsigned(0)); break;
+      case GLSL_TYPE_INT:   zero = new ir_constant(int(0));      break;
+      case GLSL_TYPE_FLOAT: zero = new ir_constant(0.0f);        break;
+      }
 
-      return new ir_expression(ir_binop_nequal, desired_type, src, zero);
+      result = new ir_expression(ir_binop_nequal, desired_type, src, zero);
    }
    }
 
-   assert(!"Should not get here.");
-   return NULL;
+   assert(result != NULL);
+
+   ir_constant *const constant = result->constant_expression_value();
+   return (constant != NULL) ? (ir_rvalue *) constant : (ir_rvalue *) result;
 }
 
 
@@ -207,7 +215,7 @@ dereference_component(ir_rvalue *src, unsigned component)
        */
       const int c = component / src->type->column_type()->vector_elements;
       const int r = component % src->type->column_type()->vector_elements;
-      ir_constant *const col_index = new ir_constant(glsl_type::int_type, &c);
+      ir_constant *const col_index = new ir_constant(c);
       ir_dereference *const col = new ir_dereference_array(src, col_index);
 
       col->type = src->type->column_type();
@@ -290,6 +298,144 @@ process_array_constructor(exec_list *instructions,
 }
 
 
+/**
+ * Try to convert a record constructor to a constant expression
+ */
+static ir_constant *
+constant_record_constructor(const glsl_type *constructor_type,
+                           YYLTYPE *loc, exec_list *parameters,
+                           struct _mesa_glsl_parse_state *state)
+{
+   bool all_parameters_are_constant = true;
+
+   exec_node *node = parameters->head;
+   for (unsigned i = 0; i < constructor_type->length; i++) {
+      ir_instruction *ir = (ir_instruction *) node;
+
+      if (node->is_tail_sentinal()) {
+        _mesa_glsl_error(loc, state,
+                         "insufficient parameters to constructor for `%s'",
+                         constructor_type->name);
+        return NULL;
+      }
+
+      if (ir->type != constructor_type->fields.structure[i].type) {
+        _mesa_glsl_error(loc, state,
+                         "parameter type mismatch in constructor for `%s' "
+                         " (%s vs %s)",
+                         constructor_type->name,
+                         ir->type->name,
+                         constructor_type->fields.structure[i].type->name);
+        return NULL;
+      }
+
+      if (ir->as_constant() == NULL)
+        all_parameters_are_constant = false;
+
+      node = node->next;
+   }
+
+   if (!all_parameters_are_constant)
+      return NULL;
+
+   return new ir_constant(constructor_type, parameters);
+}
+
+
+/**
+ * Generate data for a constant matrix constructor w/a single scalar parameter
+ *
+ * Matrix constructors in GLSL can be passed a single scalar of the
+ * approriate type.  In these cases, the resulting matrix is the identity
+ * matrix multipled by the specified scalar.  This function generates data for
+ * that matrix.
+ *
+ * \param type         Type of the desired matrix.
+ * \param initializer  Scalar value used to initialize the matrix diagonal.
+ * \param data         Location to store the resulting matrix.
+ */
+void
+generate_constructor_matrix(const glsl_type *type, ir_constant *initializer,
+                           ir_constant_data *data)
+{
+   switch (type->base_type) {
+   case GLSL_TYPE_UINT:
+   case GLSL_TYPE_INT:
+      for (unsigned i = 0; i < type->components(); i++)
+        data->u[i] = 0;
+
+      for (unsigned i = 0; i < type->matrix_columns; i++) {
+        /* The array offset of the ith row and column of the matrix.
+         */
+        const unsigned idx = (i * type->vector_elements) + i;
+
+        data->u[idx] = initializer->value.u[0];
+      }
+      break;
+
+   case GLSL_TYPE_FLOAT:
+      for (unsigned i = 0; i < type->components(); i++)
+        data->f[i] = 0;
+
+      for (unsigned i = 0; i < type->matrix_columns; i++) {
+        /* The array offset of the ith row and column of the matrix.
+         */
+        const unsigned idx = (i * type->vector_elements) + i;
+
+        data->f[idx] = initializer->value.f[0];
+      }
+
+      break;
+
+   default:
+      assert(!"Should not get here.");
+      break;
+   }
+}
+
+
+/**
+ * Generate data for a constant vector constructor w/a single scalar parameter
+ *
+ * Vector constructors in GLSL can be passed a single scalar of the
+ * approriate type.  In these cases, the resulting vector contains the specified
+ * value in all components.  This function generates data for that vector.
+ *
+ * \param type         Type of the desired vector.
+ * \param initializer  Scalar value used to initialize the vector.
+ * \param data         Location to store the resulting vector data.
+ */
+void
+generate_constructor_vector(const glsl_type *type, ir_constant *initializer,
+                           ir_constant_data *data)
+{
+   switch (type->base_type) {
+   case GLSL_TYPE_UINT:
+   case GLSL_TYPE_INT:
+      for (unsigned i = 0; i < type->components(); i++)
+        data->u[i] = initializer->value.u[0];
+
+      break;
+
+   case GLSL_TYPE_FLOAT:
+      for (unsigned i = 0; i < type->components(); i++)
+        data->f[i] = initializer->value.f[0];
+
+      break;
+
+   case GLSL_TYPE_BOOL:
+      for (unsigned i = 0; i < type->components(); i++)
+        data->b[i] = initializer->value.b[0];
+
+      break;
+
+   default:
+      assert(!"Should not get here.");
+      break;
+   }
+}
+
+
 ir_rvalue *
 ast_function_expression::hir(exec_list *instructions,
                             struct _mesa_glsl_parse_state *state)
@@ -367,12 +513,20 @@ ast_function_expression::hir(exec_list *instructions,
 
         bool all_parameters_are_constant = true;
 
-        assert(!this->expressions.is_empty());
+        /* This handles invalid constructor calls such as 'vec4 v = vec4();'
+         */
+        if (this->expressions.is_empty()) {
+           _mesa_glsl_error(& loc, state, "too few components to construct "
+                            "`%s'",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
 
         foreach_list (n, &this->expressions) {
            ast_node *ast = exec_node_data(ast_node, n, link);
            ir_rvalue *result =
               ast->hir(instructions, state)->as_rvalue();
+           ir_variable *result_var = NULL;
 
            /* Attempt to convert the parameter to a constant valued expression.
             * After doing so, track whether or not all the parameters to the
@@ -413,6 +567,19 @@ ast_function_expression::hir(exec_list *instructions,
            else
               nonmatrix_parameters++;
 
+           /* We can't use the same instruction node in the multiple
+            * swizzle dereferences that happen, so assign it to a
+            * variable and deref that.  Plus it saves computation for
+            * complicated expressions and handles
+            * glsl-vs-constructor-call.shader_test.
+            */
+           if (result->type->components() >= 1 && !result->as_constant()) {
+              result_var = new ir_variable(result->type, "constructor_tmp");
+              ir_dereference_variable *lhs;
+
+              lhs = new ir_dereference_variable(result_var);
+              instructions->push_tail(new ir_assignment(lhs, result, NULL));
+           }
 
            /* Process each of the components of the parameter.  Dereference
             * each component individually, perform any type conversions, and
@@ -422,9 +589,15 @@ ast_function_expression::hir(exec_list *instructions,
               if (components_used >= type_components)
                  break;
 
-              ir_rvalue *const component =
-                 convert_component(dereference_component(result, i),
-                                   base_type);
+              ir_rvalue *component;
+
+              if (result_var) {
+                 ir_dereference *d = new ir_dereference_variable(result_var);
+                 component = dereference_component(d, i);
+              } else {
+                 component = dereference_component(result, i);
+              }
+              component = convert_component(component, base_type);
 
               /* All cases that could result in component->type being the
                * error type should have already been caught above.
@@ -499,13 +672,31 @@ ast_function_expression::hir(exec_list *instructions,
            /* If all of the parameters are trivially constant, create a
             * constant representing the complete collection of parameters.
             */
-           if (all_parameters_are_constant
-               && (sig->return_type->is_scalar()
-                   || sig->return_type->is_vector()
-                   || sig->return_type->is_matrix())
-               && (components_used >= type_components))
-              return new ir_constant(sig->return_type, & actual_parameters);
-           else
+           if (all_parameters_are_constant) {
+              if (components_used >= type_components)
+                 return new ir_constant(sig->return_type, & actual_parameters);
+
+              assert(sig->return_type->is_vector()
+                     || sig->return_type->is_matrix());
+
+              /* Constructors with exactly one component are special for
+               * vectors and matrices.  For vectors it causes all elements of
+               * the vector to be filled with the value.  For matrices it
+               * causes the matrix to be filled with 0 and the diagonal to be
+               * filled with the value.
+               */
+              ir_constant_data data;
+              ir_constant *const initializer =
+                 (ir_constant *) actual_parameters.head;
+              if (sig->return_type->is_matrix())
+                 generate_constructor_matrix(sig->return_type, initializer,
+                                             &data);
+              else
+                 generate_constructor_vector(sig->return_type, initializer,
+                                             &data);
+
+              return new ir_constant(sig->return_type, &data);
+           } else
               return new ir_call(sig, & actual_parameters);
         } else {
            /* FINISHME: Log a better error message here.  G++ will show the
@@ -523,10 +714,25 @@ ast_function_expression::hir(exec_list *instructions,
    } else {
       const ast_expression *id = subexpressions[0];
       YYLTYPE loc = id->get_location();
+      exec_list actual_parameters;
+
+      process_parameters(instructions, &actual_parameters, &this->expressions,
+                        state);
+
+      const glsl_type *const type =
+        state->symbols->get_type(id->primary_expression.identifier);
+
+      if ((type != NULL) && type->is_record()) {
+        ir_constant *constant =
+           constant_record_constructor(type, &loc, &actual_parameters, state);
+
+        if (constant != NULL)
+           return constant;
+      }
 
       return match_function_by_name(instructions, 
                                    id->primary_expression.identifier, & loc,
-                                   &this->expressions, state);
+                                   &actual_parameters, state);
    }
 
    return ir_call::get_error_instruction();