From 0b7dcc80eb3163b46d40c5175a27a2ea4264fcd3 Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Fri, 26 Mar 2010 17:38:58 -0700 Subject: [PATCH] Initial implementation of constructor handling code 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 | 240 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/ast_function.cpp b/ast_function.cpp index f774a2fd776..6470057a905 100644 --- a/ast_function.cpp +++ b/ast_function.cpp @@ -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 { -- 2.30.2