Initial implementation of constructor handling code
authorIan Romanick <ian.d.romanick@intel.com>
Sat, 27 Mar 2010 00:38:58 +0000 (17:38 -0700)
committerIan Romanick <ian.d.romanick@intel.com>
Sat, 27 Mar 2010 00:38:58 +0000 (17:38 -0700)
All of the scalar, vector, and matrix constructors *except* "from
bool" constructors should be handled.  Array and structure
constructors are also not yet handled.

ast_function.cpp

index f774a2fd776abc07a58cd358572b82348e636b99..6470057a905e7e522907861d03b89878c8c3f2ac 100644 (file)
@@ -78,6 +78,89 @@ match_function_by_name(exec_list *instructions, const char *name,
 }
 
 
+/**
+ * Perform automatic type conversion of constructor parameters
+ */
+static ir_rvalue *
+convert_component(ir_rvalue *src, const glsl_type *desired_type)
+{
+   const unsigned a = desired_type->base_type;
+   const unsigned b = src->type->base_type;
+
+   if (src->type->is_error())
+      return src;
+
+   assert(a <= GLSL_TYPE_BOOL);
+   assert(b <= GLSL_TYPE_BOOL);
+
+   if ((a == b) || (src->type->is_integer() && desired_type->is_integer()))
+      return src;
+
+   switch (a) {
+   case GLSL_TYPE_UINT:
+   case GLSL_TYPE_INT:
+      if (b == GLSL_TYPE_FLOAT)
+        return new ir_expression(ir_unop_f2i, desired_type, src, NULL);
+      else {
+        assert(b == GLSL_TYPE_BOOL);
+        assert(!"FINISHME: Convert bool to int / uint.");
+      }
+   case GLSL_TYPE_FLOAT:
+      switch (b) {
+      case GLSL_TYPE_UINT:
+        return new ir_expression(ir_unop_u2f, desired_type, src, NULL);
+      case GLSL_TYPE_INT:
+        return new ir_expression(ir_unop_i2f, desired_type, src, NULL);
+      case GLSL_TYPE_BOOL:
+        assert(!"FINISHME: Convert bool to float.");
+      }
+      break;
+   case GLSL_TYPE_BOOL: {
+      int z = 0;
+      ir_constant *const zero = new ir_constant(src->type, &z);
+
+      return new ir_expression(ir_binop_nequal, desired_type, src, zero);
+   }
+   }
+
+   assert(!"Should not get here.");
+   return NULL;
+}
+
+
+/**
+ * Dereference a specific component from a scalar, vector, or matrix
+ */
+static ir_rvalue *
+dereference_component(ir_rvalue *src, unsigned component)
+{
+   assert(component < src->type->components());
+
+   if (src->type->is_scalar()) {
+      return src;
+   } else if (src->type->is_vector()) {
+      return new ir_swizzle(src, component, 0, 0, 0, 1);
+   } else {
+      assert(src->type->is_matrix());
+
+      /* Dereference a row of the matrix, then call this function again to get
+       * a specific element from that row.
+       */
+      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_dereference *const col = new ir_dereference(src, col_index);
+
+      col->type = src->type->column_type();
+
+      return dereference_component(col, r);
+   }
+
+   assert(!"Should not get here.");
+   return NULL;
+}
+
+
 ir_rvalue *
 ast_function_expression::hir(exec_list *instructions,
                             struct _mesa_glsl_parse_state *state)
@@ -116,6 +199,163 @@ ast_function_expression::hir(exec_list *instructions,
        * correct order.  These constructors follow essentially the same type
        * matching rules as functions.
        */
+      if (constructor_type->is_numeric() || constructor_type->is_boolean()) {
+        /* Constructing a numeric type has a couple steps.  First all values
+         * passed to the constructor are broken into individual parameters
+         * and type converted to the base type of the thing being constructed.
+         *
+         * At that point we have some number of values that match the base
+         * type of the thing being constructed.  Now the constructor can be
+         * treated like a function call.  Each numeric type has a small set
+         * of constructor functions.  The set of new parameters will either
+         * match one of those functions or the original constructor is
+         * invalid.
+         */
+        const glsl_type *const base_type = constructor_type->get_base_type();
+
+        /* Total number of components of the type being constructed.
+         */
+        const unsigned type_components = constructor_type->components();
+
+        /* Number of components from parameters that have actually been
+         * consumed.  This is used to perform several kinds of error checking.
+         */
+        unsigned components_used = 0;
+
+        unsigned matrix_parameters = 0;
+        unsigned nonmatrix_parameters = 0;
+        exec_list actual_parameters;
+        simple_node *const first = subexpressions[1];
+
+        assert(first != NULL);
+
+        if (first != NULL) {
+           simple_node *ptr = first;
+           do {
+              ir_rvalue *const result =
+                 ((ast_node *) ptr)->hir(instructions, state)->as_rvalue();
+              ptr = ptr->next;
+
+              /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec:
+               *
+               *    "It is an error to provide extra arguments beyond this
+               *    last used argument."
+               */
+              if (components_used >= type_components) {
+                 _mesa_glsl_error(& loc, state, "too many parameters to `%s' "
+                                  "constructor",
+                                  constructor_type->name);
+                 return ir_call::get_error_instruction();
+              }
+
+              if (!result->type->is_numeric() && !result->type->is_boolean()) {
+                 _mesa_glsl_error(& loc, state, "cannot construct `%s' from a "
+                                  "non-numeric data type",
+                                  constructor_type->name);
+                 return ir_call::get_error_instruction();
+              }
+
+              /* Count the number of matrix and nonmatrix parameters.  This
+               * is used below to enforce some of the constructor rules.
+               */
+              if (result->type->is_matrix())
+                 matrix_parameters++;
+              else
+                 nonmatrix_parameters++;
+
+
+              /* Process each of the components of the parameter.  Dereference
+               * each component individually, perform any type conversions, and
+               * add it to the parameter list for the constructor.
+               */
+              for (unsigned i = 0; i < result->type->components(); i++) {
+                 if (components_used >= type_components)
+                    break;
+
+                 ir_rvalue *const component =
+                    convert_component(dereference_component(result, i),
+                                      base_type);
+
+                 /* All cases that could result in component->type being the
+                  * error type should have already been caught above.
+                  */
+                 assert(component->type == base_type);
+
+                 /* Don't actually generate constructor calls for scalars.
+                  * Instead, do the usual component selection and conversion,
+                  * and return the single component.
+                  */
+                 if (constructor_type->is_scalar())
+                    return component;
+
+                 actual_parameters.push_tail(component);
+                 components_used++;
+              }
+           } while (ptr != first);
+        }
+
+        /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec:
+         *
+         *    "It is an error to construct matrices from other matrices. This
+         *    is reserved for future use."
+         */
+        if ((state->language_version <= 110) && (matrix_parameters > 0)
+            && constructor_type->is_matrix()) {
+           _mesa_glsl_error(& loc, state, "cannot construct `%s' from a "
+                            "matrix in GLSL 1.10",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
+
+        /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec:
+         *
+         *    "If a matrix argument is given to a matrix constructor, it is
+         *    an error to have any other arguments."
+         */
+        if ((matrix_parameters > 0)
+            && ((matrix_parameters + nonmatrix_parameters) > 1)
+            && constructor_type->is_matrix()) {
+           _mesa_glsl_error(& loc, state, "for matrix `%s' constructor, "
+                            "matrix must be only parameter",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
+
+        /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec:
+         *
+         *    "In these cases, there must be enough components provided in the
+         *    arguments to provide an initializer for every component in the
+         *    constructed value."
+         */
+        if (components_used < type_components) {
+           _mesa_glsl_error(& loc, state, "too few components to construct "
+                            "`%s'",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
+
+        ir_function *f = state->symbols->get_function(constructor_type->name);
+        if (f == NULL) {
+           _mesa_glsl_error(& loc, state, "no constructor for type `%s'",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
+
+        const ir_function_signature *sig =
+           f->matching_signature(& actual_parameters);
+        if (sig != NULL) {
+           return new ir_call(sig, & actual_parameters);
+        } else {
+           /* FINISHME: Log a better error message here.  G++ will show the
+            * FINSIHME: types of the actual parameters and the set of
+            * FINSIHME: candidate functions.  A different error should also be
+            * FINSIHME: logged when multiple functions match.
+            */
+           _mesa_glsl_error(& loc, state, "no matching constructor for `%s'",
+                            constructor_type->name);
+           return ir_call::get_error_instruction();
+        }
+      }
 
       return ir_call::get_error_instruction();
    } else {