X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Fast_function.cpp;h=39401017b81439ca71111b9abcb97b734c02334f;hb=071501a68129768c6223beb24f7363d87c6684ea;hp=bbc3bc1a5969c30a46d19ff5b2db1d855731a1f0;hpb=62c4763b707e2227409f81b09dd5cf6e4410ea6a;p=mesa.git diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp index bbc3bc1a596..39401017b81 100644 --- a/src/glsl/ast_function.cpp +++ b/src/glsl/ast_function.cpp @@ -25,15 +25,15 @@ #include "ast.h" #include "glsl_types.h" #include "ir.h" - -inline unsigned min(unsigned a, unsigned b) -{ - return (a < b) ? a : b; -} +#include "main/core.h" /* for MIN2 */ static ir_rvalue * convert_component(ir_rvalue *src, const glsl_type *desired_type); +bool +apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from, + struct _mesa_glsl_parse_state *state); + static unsigned process_parameters(exec_list *instructions, exec_list *actual_parameters, exec_list *parameters, @@ -57,112 +57,367 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters, } -static ir_rvalue * -process_call(exec_list *instructions, ir_function *f, - YYLTYPE *loc, exec_list *actual_parameters, - struct _mesa_glsl_parse_state *state) +/** + * Generate a source prototype for a function signature + * + * \param return_type Return type of the function. May be \c NULL. + * \param name Name of the function. + * \param parameters List of \c ir_instruction nodes representing the + * parameter list for the function. This may be either a + * formal (\c ir_variable) or actual (\c ir_rvalue) + * parameter list. Only the type is used. + * + * \return + * A ralloced string representing the prototype of the function. + */ +char * +prototype_string(const glsl_type *return_type, const char *name, + exec_list *parameters) { - void *ctx = state; + char *str = NULL; - ir_function_signature *sig = f->matching_signature(actual_parameters); + if (return_type != NULL) + str = ralloc_asprintf(NULL, "%s ", return_type->name); - /* The instructions param will be used when the FINISHMEs below are done */ - (void) instructions; + ralloc_asprintf_append(&str, "%s(", name); - 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. + const char *comma = ""; + foreach_list(node, parameters) { + const ir_variable *const param = (ir_variable *) node; + + ralloc_asprintf_append(&str, "%s%s", comma, param->type->name); + comma = ", "; + } + + ralloc_strcat(&str, ")"); + return str; +} + +/** + * Verify that 'out' and 'inout' actual parameters are lvalues. Also, verify + * that 'const_in' formal parameters (an extension in our IR) correspond to + * ir_constant actual parameters. + */ +static bool +verify_parameter_modes(_mesa_glsl_parse_state *state, + ir_function_signature *sig, + exec_list &actual_ir_parameters, + exec_list &actual_ast_parameters) +{ + exec_node *actual_ir_node = actual_ir_parameters.head; + exec_node *actual_ast_node = actual_ast_parameters.head; + + foreach_list(formal_node, &sig->parameters) { + /* The lists must be the same length. */ + assert(!actual_ir_node->is_tail_sentinel()); + assert(!actual_ast_node->is_tail_sentinel()); + + const ir_variable *const formal = (ir_variable *) formal_node; + const ir_rvalue *const actual = (ir_rvalue *) actual_ir_node; + const ast_expression *const actual_ast = + exec_node_data(ast_expression, actual_ast_node, link); + + /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always + * FIXME: 0:0(0). */ - exec_list_iterator actual_iter = actual_parameters->iterator(); - exec_list_iterator formal_iter = sig->parameters.iterator(); + YYLTYPE loc = actual_ast->get_location(); + + /* Verify that 'const_in' parameters are ir_constants. */ + if (formal->mode == ir_var_const_in && + actual->ir_type != ir_type_constant) { + _mesa_glsl_error(&loc, state, + "parameter `in %s' must be a constant expression", + formal->name); + return false; + } - while (actual_iter.has_next()) { - ir_rvalue *actual = (ir_rvalue *) actual_iter.get(); - ir_variable *formal = (ir_variable *) formal_iter.get(); + /* Verify that 'out' and 'inout' actual parameters are lvalues. */ + 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; + } - assert(actual != NULL); - assert(formal != NULL); + /* This AST-based check catches errors like f(i++). The IR-based + * is_lvalue() is insufficient because the actual parameter at the + * IR-level is just a temporary value, which is an l-value. + */ + if (actual_ast->non_lvalue_description != NULL) { + _mesa_glsl_error(&loc, state, + "function parameter '%s %s' references a %s", + mode, formal->name, + actual_ast->non_lvalue_description); + return false; + } - if ((formal->mode == ir_var_out) - || (formal->mode == ir_var_inout)) { - if (! actual->is_lvalue()) { - /* FINISHME: Log a better diagnostic here. There is no way - * FINISHME: to tell the user which parameter is invalid. - */ - _mesa_glsl_error(loc, state, "`%s' parameter is not lvalue", - (formal->mode == ir_var_out) ? "out" : "inout"); - } + 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); + return false; + } else if (!actual->is_lvalue()) { + _mesa_glsl_error(&loc, state, + "function parameter '%s %s' is not an lvalue", + mode, formal->name); + return false; } + } - if (formal->type->is_numeric() || formal->type->is_boolean()) { - ir_rvalue *converted = convert_component(actual, formal->type); + actual_ir_node = actual_ir_node->next; + actual_ast_node = actual_ast_node->next; + } + return true; +} + +/** + * If a function call is generated, \c call_ir will point to it on exit. + * Otherwise \c call_ir will be set to \c NULL. + */ +static ir_rvalue * +generate_call(exec_list *instructions, ir_function_signature *sig, + YYLTYPE *loc, exec_list *actual_parameters, + ir_call **call_ir, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + exec_list post_call_conversions; + + *call_ir = NULL; + + /* Perform implicit conversion of arguments. For 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->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. - */ - ir_call *call = new(ctx) ir_call(sig, actual_parameters); - if (!sig->return_type->is_void()) { - ir_variable *var; - ir_dereference_variable *deref; - - var = new(ctx) ir_variable(sig->return_type, - talloc_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); - if (state->language_version >= 120) - var->constant_value = call->constant_expression_value(); - - deref = new(ctx) ir_dereference_variable(var); - return deref; - } else { - instructions->push_tail(call); - return NULL; + actual_iter.next(); + formal_iter.next(); + } + + /* If the function call is a constant expression, don't generate any + * instructions; just generate an ir_constant. + * + * Function calls were first allowed to be constant expressions in GLSL 1.20. + */ + if (state->language_version >= 120) { + ir_constant *value = sig->constant_expression_value(actual_parameters); + if (value != NULL) { + return value; } - } else { - /* FINISHME: Log a better error message here. G++ will show the types - * FINISHME: of the actual parameters and the set of candidate - * FINISHME: functions. A different error should also be logged when - * FINISHME: multiple functions match. - */ - _mesa_glsl_error(loc, state, "no matching function for call to `%s'", - f->name); - return ir_call::get_error_instruction(ctx); } -} + ir_dereference_variable *deref = NULL; + if (!sig->return_type->is_void()) { + /* Create a new temporary to hold the return value. */ + ir_variable *var; -static ir_rvalue * -match_function_by_name(exec_list *instructions, const char *name, - YYLTYPE *loc, exec_list *actual_parameters, + 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_call *call = new(ctx) ir_call(sig, deref, actual_parameters); + instructions->push_tail(call); + + /* Also emit any necessary out-parameter conversions. */ + instructions->append_list(&post_call_conversions); + + return deref ? deref->clone(ctx, NULL) : NULL; +} + +/** + * Given a function name and parameter list, find the matching signature. + */ +static ir_function_signature * +match_function_by_name(const char *name, + exec_list *actual_parameters, struct _mesa_glsl_parse_state *state) { void *ctx = state; ir_function *f = state->symbols->get_function(name); + ir_function_signature *local_sig = NULL; + ir_function_signature *sig = NULL; + + /* Is the function hidden by a record type constructor? */ + if (state->symbols->get_type(name)) + goto done; /* no match */ + + /* Is the function hidden by a variable (impossible in 1.10)? */ + if (state->language_version != 110 && state->symbols->get_variable(name)) + goto done; /* no match */ + + if (f != NULL) { + /* Look for a match in the local shader. If exact, we're done. */ + bool is_exact = false; + sig = local_sig = f->matching_signature(actual_parameters, &is_exact); + if (is_exact) + goto done; + + if (!state->es_shader && f->has_user_signature()) { + /* In desktop GL, the presence of a user-defined signature hides any + * built-in signatures, so we must ignore them. In contrast, in ES2 + * user-defined signatures add new overloads, so we must proceed. + */ + goto done; + } + } + + /* Local shader has no exact candidates; check the built-ins. */ + _mesa_glsl_initialize_functions(state); + for (unsigned i = 0; i < state->num_builtins_to_link; i++) { + ir_function *builtin = + state->builtins_to_link[i]->symbols->get_function(name); + if (builtin == NULL) + continue; + + bool is_exact = false; + ir_function_signature *builtin_sig = + builtin->matching_signature(actual_parameters, &is_exact); + + if (builtin_sig == NULL) + continue; + + /* If the built-in signature is exact, we can stop. */ + if (is_exact) { + sig = builtin_sig; + goto done; + } - if (f == NULL) { - _mesa_glsl_error(loc, state, "function `%s' undeclared", name); - return ir_call::get_error_instruction(ctx); + if (sig == NULL) { + /* We found an inexact match, which is better than nothing. However, + * we should keep searching for an exact match. + */ + sig = builtin_sig; + } } - /* 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); +done: + if (sig != NULL) { + /* If the match is from a linked built-in shader, import the prototype. */ + if (sig != local_sig) { + if (f == NULL) { + f = new(ctx) ir_function(name); + state->symbols->add_global_function(f); + emit_function(state, f); + } + f->add_signature(sig->clone_prototype(f, NULL)); + } + } + return sig; } +/** + * Raise a "no matching function" error, listing all possible overloads the + * compiler considered so developers can figure out what went wrong. + */ +static void +no_matching_function_error(const char *name, + YYLTYPE *loc, + exec_list *actual_parameters, + _mesa_glsl_parse_state *state) +{ + char *str = prototype_string(NULL, name, actual_parameters); + _mesa_glsl_error(loc, state, "no matching function for call to `%s'", str); + ralloc_free(str); + + const char *prefix = "candidates are: "; + + for (int i = -1; i < (int) state->num_builtins_to_link; i++) { + glsl_symbol_table *syms = i >= 0 ? state->builtins_to_link[i]->symbols + : state->symbols; + ir_function *f = syms->get_function(name); + if (f == NULL) + continue; + + foreach_list (node, &f->signatures) { + ir_function_signature *sig = (ir_function_signature *) node; + + str = prototype_string(sig->return_type, f->name, &sig->parameters); + _mesa_glsl_error(loc, state, "%s%s", prefix, str); + ralloc_free(str); + + prefix = " "; + } + } +} /** * Perform automatic type conversion of constructor parameters @@ -173,7 +428,7 @@ match_function_by_name(exec_list *instructions, const char *name, static ir_rvalue * convert_component(ir_rvalue *src, const glsl_type *desired_type) { - void *ctx = talloc_parent(src); + void *ctx = ralloc_parent(src); const unsigned a = desired_type->base_type; const unsigned b = src->type->base_type; ir_expression *result = NULL; @@ -184,17 +439,36 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type) assert(a <= GLSL_TYPE_BOOL); assert(b <= GLSL_TYPE_BOOL); - if ((a == b) || (src->type->is_integer() && desired_type->is_integer())) + if (a == b) return src; switch (a) { case GLSL_TYPE_UINT: + switch (b) { + case GLSL_TYPE_INT: + result = new(ctx) ir_expression(ir_unop_i2u, src); + break; + case GLSL_TYPE_FLOAT: + result = new(ctx) ir_expression(ir_unop_i2u, + new(ctx) ir_expression(ir_unop_f2i, src)); + break; + case GLSL_TYPE_BOOL: + result = new(ctx) ir_expression(ir_unop_i2u, + new(ctx) ir_expression(ir_unop_b2i, src)); + break; + } + break; case GLSL_TYPE_INT: - if (b == GLSL_TYPE_FLOAT) - result = new(ctx) ir_expression(ir_unop_f2i, desired_type, src, NULL); - else { - assert(b == GLSL_TYPE_BOOL); - result = new(ctx) ir_expression(ir_unop_b2i, desired_type, src, NULL); + switch (b) { + case GLSL_TYPE_UINT: + result = new(ctx) ir_expression(ir_unop_u2i, src); + break; + case GLSL_TYPE_FLOAT: + result = new(ctx) ir_expression(ir_unop_f2i, src); + break; + case GLSL_TYPE_BOOL: + result = new(ctx) ir_expression(ir_unop_b2i, src); + break; } break; case GLSL_TYPE_FLOAT: @@ -213,6 +487,9 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type) case GLSL_TYPE_BOOL: switch (b) { case GLSL_TYPE_UINT: + result = new(ctx) ir_expression(ir_unop_i2b, + new(ctx) ir_expression(ir_unop_u2i, src)); + break; case GLSL_TYPE_INT: result = new(ctx) ir_expression(ir_unop_i2b, desired_type, src, NULL); break; @@ -224,6 +501,7 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type) } assert(result != NULL); + assert(result->type == desired_type); /* Try constant folding; it may fold in the conversion we just added. */ ir_constant *const constant = result->constant_expression_value(); @@ -236,7 +514,7 @@ convert_component(ir_rvalue *src, const glsl_type *desired_type) static ir_rvalue * dereference_component(ir_rvalue *src, unsigned component) { - void *ctx = talloc_parent(src); + void *ctx = ralloc_parent(src); assert(component < src->type->components()); /* If the source is a constant, just create a new constant instead of a @@ -312,7 +590,7 @@ process_array_constructor(exec_list *instructions, "parameter%s", (constructor_type->length != 0) ? "at least" : "exactly", min_param, (min_param <= 1) ? "" : "s"); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } if (constructor_type->length == 0) { @@ -330,13 +608,21 @@ process_array_constructor(exec_list *instructions, ir_rvalue *ir = (ir_rvalue *) n; ir_rvalue *result = ir; - /* Apply implicit conversions (not the scalar constructor rules!) */ + /* Apply implicit conversions (not the scalar constructor rules!). See + * the spec quote above. */ if (constructor_type->element_type()->is_float()) { const glsl_type *desired_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, ir->type->vector_elements, ir->type->matrix_columns); - result = convert_component(ir, desired_type); + if (result->type->can_implicitly_convert_to(desired_type)) { + /* Even though convert_component() implements the constructor + * conversion rules (not the implicit conversion rules), its safe + * to use it here because we already checked that the implicit + * conversion is legal. + */ + result = convert_component(ir, desired_type); + } } if (result->type != constructor_type->element_type()) { @@ -388,137 +674,16 @@ process_array_constructor(exec_list *instructions, */ static ir_constant * constant_record_constructor(const glsl_type *constructor_type, - YYLTYPE *loc, exec_list *parameters, - struct _mesa_glsl_parse_state *state) + exec_list *parameters, void *mem_ctx) { - void *ctx = 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_sentinel()) { - _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); + foreach_list(node, parameters) { + ir_constant *constant = ((ir_instruction *) node)->as_constant(); + if (constant == NULL) return NULL; - } - - if (ir->as_constant() == NULL) - all_parameters_are_constant = false; - - node = node->next; + node->replace_with(constant); } - if (!all_parameters_are_constant) - return NULL; - - return new(ctx) 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; - } + return new(mem_ctx) ir_constant(constructor_type, parameters); } @@ -554,9 +719,7 @@ emit_inline_vector_constructor(const glsl_type *type, { assert(!parameters->is_empty()); - ir_variable *var = new(ctx) ir_variable(type, - talloc_strdup(ctx, "vec_ctor"), - ir_var_temporary); + ir_variable *var = new(ctx) ir_variable(type, "vec_ctor", ir_var_temporary); instructions->push_tail(var); /* There are two kinds of vector constructors. @@ -574,37 +737,106 @@ emit_inline_vector_constructor(const glsl_type *type, ir_rvalue *rhs = new(ctx) ir_swizzle(first_param, 0, 0, 0, 0, lhs_components); ir_dereference_variable *lhs = new(ctx) ir_dereference_variable(var); + const unsigned mask = (1U << lhs_components) - 1; assert(rhs->type == lhs->type); - ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL); + ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL, mask); instructions->push_tail(inst); } else { unsigned base_component = 0; + unsigned base_lhs_component = 0; + ir_constant_data data; + unsigned constant_mask = 0, constant_components = 0; + + memset(&data, 0, sizeof(data)); + foreach_list(node, parameters) { - ir_rvalue *rhs = (ir_rvalue *) node; - unsigned rhs_components = rhs->type->components(); + ir_rvalue *param = (ir_rvalue *) node; + unsigned rhs_components = param->type->components(); /* Do not try to assign more components to the vector than it has! */ - if ((rhs_components + base_component) > lhs_components) { - rhs_components = lhs_components - base_component; + if ((rhs_components + base_lhs_component) > lhs_components) { + rhs_components = lhs_components - base_lhs_component; } - /* Emit an assignment of the constructor parameter to the next set of - * components in the temporary variable. + const ir_constant *const c = param->as_constant(); + if (c != NULL) { + for (unsigned i = 0; i < rhs_components; i++) { + switch (c->type->base_type) { + case GLSL_TYPE_UINT: + data.u[i + base_component] = c->get_uint_component(i); + break; + case GLSL_TYPE_INT: + data.i[i + base_component] = c->get_int_component(i); + break; + case GLSL_TYPE_FLOAT: + data.f[i + base_component] = c->get_float_component(i); + break; + case GLSL_TYPE_BOOL: + data.b[i + base_component] = c->get_bool_component(i); + break; + default: + assert(!"Should not get here."); + break; + } + } + + /* Mask of fields to be written in the assignment. + */ + constant_mask |= ((1U << rhs_components) - 1) << base_lhs_component; + constant_components += rhs_components; + + base_component += rhs_components; + } + /* Advance the component index by the number of components + * that were just assigned. */ - unsigned mask[4] = { 0, 0, 0, 0 }; - for (unsigned i = 0; i < rhs_components; i++) { - mask[i] = i + base_component; + base_lhs_component += rhs_components; + } + + if (constant_mask != 0) { + ir_dereference *lhs = new(ctx) ir_dereference_variable(var); + const glsl_type *rhs_type = glsl_type::get_instance(var->type->base_type, + constant_components, + 1); + ir_rvalue *rhs = new(ctx) ir_constant(rhs_type, &data); + + ir_instruction *inst = + new(ctx) ir_assignment(lhs, rhs, NULL, constant_mask); + instructions->push_tail(inst); + } + + base_component = 0; + foreach_list(node, parameters) { + ir_rvalue *param = (ir_rvalue *) node; + unsigned rhs_components = param->type->components(); + + /* Do not try to assign more components to the vector than it has! + */ + if ((rhs_components + base_component) > lhs_components) { + rhs_components = lhs_components - base_component; } + const ir_constant *const c = param->as_constant(); + if (c == NULL) { + /* Mask of fields to be written in the assignment. + */ + const unsigned write_mask = ((1U << rhs_components) - 1) + << base_component; - ir_rvalue *lhs_ref = new(ctx) ir_dereference_variable(var); - ir_swizzle *lhs = new(ctx) ir_swizzle(lhs_ref, mask, rhs_components); + ir_dereference *lhs = new(ctx) ir_dereference_variable(var); - ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL); - instructions->push_tail(inst); + /* Generate a swizzle so that LHS and RHS sizes match. + */ + ir_rvalue *rhs = + new(ctx) ir_swizzle(param, 0, 1, 2, 3, rhs_components); + + ir_instruction *inst = + new(ctx) ir_assignment(lhs, rhs, NULL, write_mask); + instructions->push_tail(inst); + } /* Advance the component index by the number of components that were * just assigned. @@ -631,20 +863,29 @@ emit_inline_vector_constructor(const glsl_type *type, ir_instruction * assign_to_matrix_column(ir_variable *var, unsigned column, unsigned row_base, ir_rvalue *src, unsigned src_base, unsigned count, - TALLOC_CTX *ctx) + void *mem_ctx) { - const unsigned mask[8] = { 0, 1, 2, 3, 0, 0, 0, 0 }; - - ir_constant *col_idx = new(ctx) ir_constant(column); - ir_rvalue *column_ref = new(ctx) ir_dereference_array(var, col_idx); + ir_constant *col_idx = new(mem_ctx) ir_constant(column); + ir_dereference *column_ref = new(mem_ctx) ir_dereference_array(var, col_idx); assert(column_ref->type->components() >= (row_base + count)); - ir_rvalue *lhs = new(ctx) ir_swizzle(column_ref, &mask[row_base], count); - assert(src->type->components() >= (src_base + count)); - ir_rvalue *rhs = new(ctx) ir_swizzle(src, &mask[src_base], count); - return new(ctx) ir_assignment(lhs, rhs, NULL); + /* Generate a swizzle that extracts the number of components from the source + * that are to be assigned to the column of the matrix. + */ + if (count < src->type->vector_elements) { + src = new(mem_ctx) ir_swizzle(src, + src_base + 0, src_base + 1, + src_base + 2, src_base + 3, + count); + } + + /* Mask of fields to be written in the assignment. + */ + const unsigned write_mask = ((1U << count) - 1) << row_base; + + return new(mem_ctx) ir_assignment(column_ref, src, NULL, write_mask); } @@ -667,9 +908,7 @@ emit_inline_matrix_constructor(const glsl_type *type, { assert(!parameters->is_empty()); - ir_variable *var = new(ctx) ir_variable(type, - talloc_strdup(ctx, "mat_ctor"), - ir_var_temporary); + ir_variable *var = new(ctx) ir_variable(type, "mat_ctor", ir_var_temporary); instructions->push_tail(var); /* There are three kinds of matrix constructors. @@ -692,8 +931,7 @@ emit_inline_matrix_constructor(const glsl_type *type, * components with zero. */ ir_variable *rhs_var = - new(ctx) ir_variable(glsl_type::vec4_type, - talloc_strdup(ctx, "mat_ctor_vec"), + new(ctx) ir_variable(glsl_type::vec4_type, "mat_ctor_vec", ir_var_temporary); instructions->push_tail(rhs_var); @@ -709,10 +947,9 @@ emit_inline_matrix_constructor(const glsl_type *type, NULL); instructions->push_tail(inst); - ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); - ir_rvalue *const x_of_rhs = new(ctx) ir_swizzle(rhs_ref, 0, 0, 0, 0, 1); + ir_dereference *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); - inst = new(ctx) ir_assignment(x_of_rhs, first_param, NULL); + inst = new(ctx) ir_assignment(rhs_ref, first_param, NULL, 0x01); instructions->push_tail(inst); /* Assign the temporary vector to each column of the destination matrix @@ -728,8 +965,8 @@ emit_inline_matrix_constructor(const glsl_type *type, { 1, 1, 1, 0 } }; - const unsigned cols_to_init = min(type->matrix_columns, - type->vector_elements); + const unsigned cols_to_init = MIN2(type->matrix_columns, + type->vector_elements); for (unsigned i = 0; i < cols_to_init; i++) { ir_constant *const col_idx = new(ctx) ir_constant(i); ir_rvalue *const col_ref = new(ctx) ir_dereference_array(var, col_idx); @@ -807,8 +1044,7 @@ emit_inline_matrix_constructor(const glsl_type *type, * generate a temporary and copy the paramter there. */ ir_variable *const rhs_var = - new(ctx) ir_variable(first_param->type, - talloc_strdup(ctx, "mat_ctor_mat"), + new(ctx) ir_variable(first_param->type, "mat_ctor_mat", ir_var_temporary); instructions->push_tail(rhs_var); @@ -818,12 +1054,19 @@ emit_inline_matrix_constructor(const glsl_type *type, new(ctx) ir_assignment(rhs_var_ref, first_param, NULL); instructions->push_tail(inst); + const unsigned last_row = MIN2(src_matrix->type->vector_elements, + var->type->vector_elements); + const unsigned last_col = MIN2(src_matrix->type->matrix_columns, + var->type->matrix_columns); + + unsigned swiz[4] = { 0, 0, 0, 0 }; + for (unsigned i = 1; i < last_row; i++) + swiz[i] = i; + + const unsigned write_mask = (1U << last_row) - 1; - const unsigned swiz[4] = { 0, 1, 2, 3 }; - const unsigned last_col = min(src_matrix->type->matrix_columns, - var->type->matrix_columns); for (unsigned i = 0; i < last_col; i++) { - ir_rvalue *const lhs_col = + ir_dereference *const lhs = new(ctx) ir_dereference_array(var, new(ctx) ir_constant(i)); ir_rvalue *const rhs_col = new(ctx) ir_dereference_array(rhs_var, new(ctx) ir_constant(i)); @@ -836,31 +1079,20 @@ emit_inline_matrix_constructor(const glsl_type *type, * It would be perfectly valid to unconditionally generate the * swizzles, this this will typically result in a more compact IR tree. */ - ir_rvalue *lhs; ir_rvalue *rhs; - if (lhs_col->type->vector_elements < rhs_col->type->vector_elements) { - lhs = lhs_col; - - rhs = new(ctx) ir_swizzle(rhs_col, swiz, - lhs_col->type->vector_elements); - } else if (lhs_col->type->vector_elements - > rhs_col->type->vector_elements) { - lhs = new(ctx) ir_swizzle(lhs_col, swiz, - rhs_col->type->vector_elements); - rhs = rhs_col; + if (lhs->type->vector_elements != rhs_col->type->vector_elements) { + rhs = new(ctx) ir_swizzle(rhs_col, swiz, last_row); } else { - lhs = lhs_col; rhs = rhs_col; } - assert(lhs->type == rhs->type); - - ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL); + ir_instruction *inst = + new(ctx) ir_assignment(lhs, rhs, NULL, write_mask); instructions->push_tail(inst); } } else { - const unsigned rows = type->matrix_columns; - const unsigned cols = type->vector_elements; + const unsigned cols = type->matrix_columns; + const unsigned rows = type->vector_elements; unsigned col_idx = 0; unsigned row_idx = 0; @@ -874,9 +1106,7 @@ emit_inline_matrix_constructor(const glsl_type *type, * generate a temporary and copy the paramter there. */ ir_variable *rhs_var = - new(ctx) ir_variable(rhs->type, - talloc_strdup(ctx, "mat_ctor_vec"), - ir_var_temporary); + new(ctx) ir_variable(rhs->type, "mat_ctor_vec", ir_var_temporary); instructions->push_tail(rhs_var); ir_dereference *rhs_var_ref = @@ -891,8 +1121,8 @@ emit_inline_matrix_constructor(const glsl_type *type, * single vec4, for example, can completely fill a mat2. */ if (rhs_components >= components_remaining_this_column) { - const unsigned count = min(rhs_components, - components_remaining_this_column); + const unsigned count = MIN2(rhs_components, + components_remaining_this_column); rhs_var_ref = new(ctx) ir_dereference_variable(rhs_var); @@ -936,6 +1166,39 @@ emit_inline_matrix_constructor(const glsl_type *type, } +ir_rvalue * +emit_inline_record_constructor(const glsl_type *type, + exec_list *instructions, + exec_list *parameters, + void *mem_ctx) +{ + ir_variable *const var = + new(mem_ctx) ir_variable(type, "record_ctor", ir_var_temporary); + ir_dereference_variable *const d = new(mem_ctx) ir_dereference_variable(var); + + instructions->push_tail(var); + + exec_node *node = parameters->head; + for (unsigned i = 0; i < type->length; i++) { + assert(!node->is_tail_sentinel()); + + ir_dereference *const lhs = + new(mem_ctx) ir_dereference_record(d->clone(mem_ctx, NULL), + type->fields.structure[i].name); + + ir_rvalue *const rhs = ((ir_instruction *) node)->as_rvalue(); + assert(rhs != NULL); + + ir_instruction *const assign = new(mem_ctx) ir_assignment(lhs, rhs, NULL); + + instructions->push_tail(assign); + node = node->next; + } + + return d; +} + + ir_rvalue * ast_function_expression::hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) @@ -957,26 +1220,37 @@ ast_function_expression::hir(exec_list *instructions, const glsl_type *const constructor_type = type->glsl_type(& name, state); + /* constructor_type can be NULL if a variable with the same name as the + * structure has come into scope. + */ + if (constructor_type == NULL) { + _mesa_glsl_error(& loc, state, "unknown type `%s' (structure name " + "may be shadowed by a variable with the same name)", + type->type_name); + return ir_rvalue::error_value(ctx); + } + /* Constructors for samplers are illegal. */ if (constructor_type->is_sampler()) { _mesa_glsl_error(& loc, state, "cannot construct sampler type `%s'", constructor_type->name); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } if (constructor_type->is_array()) { if (state->language_version <= 110) { _mesa_glsl_error(& loc, state, "array constructors forbidden in GLSL 1.10"); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } return process_array_constructor(instructions, constructor_type, & loc, &this->expressions, state); } + /* There are two kinds of constructor call. Constructors for built-in * language types, such as mat4 and vec2, are free form. The only * requirement is that the parameters must provide enough values of the @@ -985,8 +1259,59 @@ ast_function_expression::hir(exec_list *instructions, * correct order. These constructors follow essentially the same type * matching rules as functions. */ + if (constructor_type->is_record()) { + exec_list actual_parameters; + + process_parameters(instructions, &actual_parameters, + &this->expressions, state); + + exec_node *node = actual_parameters.head; + for (unsigned i = 0; i < constructor_type->length; i++) { + ir_rvalue *ir = (ir_rvalue *) node; + + if (node->is_tail_sentinel()) { + _mesa_glsl_error(&loc, state, + "insufficient parameters to constructor " + "for `%s'", + constructor_type->name); + return ir_rvalue::error_value(ctx); + } + + if (apply_implicit_conversion(constructor_type->fields.structure[i].type, + ir, state)) { + node->replace_with(ir); + } else { + _mesa_glsl_error(&loc, state, + "parameter type mismatch in constructor " + "for `%s.%s' (%s vs %s)", + constructor_type->name, + constructor_type->fields.structure[i].name, + ir->type->name, + constructor_type->fields.structure[i].type->name); + return ir_rvalue::error_value(ctx);; + } + + node = node->next; + } + + if (!node->is_tail_sentinel()) { + _mesa_glsl_error(&loc, state, "too many parameters in constructor " + "for `%s'", constructor_type->name); + return ir_rvalue::error_value(ctx); + } + + ir_rvalue *const constant = + constant_record_constructor(constructor_type, &actual_parameters, + state); + + return (constant != NULL) + ? constant + : emit_inline_record_constructor(constructor_type, instructions, + &actual_parameters, state); + } + if (!constructor_type->is_numeric() && !constructor_type->is_boolean()) - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); /* Total number of components of the type being constructed. */ const unsigned type_components = constructor_type->components(); @@ -1013,14 +1338,14 @@ ast_function_expression::hir(exec_list *instructions, _mesa_glsl_error(& loc, state, "too many parameters to `%s' " "constructor", constructor_type->name); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } 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(ctx); + return ir_rvalue::error_value(ctx); } /* Count the number of matrix and nonmatrix parameters. This @@ -1040,12 +1365,12 @@ ast_function_expression::hir(exec_list *instructions, * "It is an error to construct matrices from other matrices. This * is reserved for future use." */ - if ((state->language_version <= 110) && (matrix_parameters > 0) + 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(ctx); + return ir_rvalue::error_value(ctx); } /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: @@ -1059,7 +1384,7 @@ ast_function_expression::hir(exec_list *instructions, _mesa_glsl_error(& loc, state, "for matrix `%s' constructor, " "matrix must be only parameter", constructor_type->name); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec: @@ -1068,11 +1393,12 @@ ast_function_expression::hir(exec_list *instructions, * arguments to provide an initializer for every component in the * constructed value." */ - if ((components_used < type_components) && (components_used != 1)) { + if (components_used < type_components && components_used != 1 + && matrix_parameters == 0) { _mesa_glsl_error(& loc, state, "too few components to construct " "`%s'", constructor_type->name); - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); } /* Later, we cast each parameter to the same base type as the @@ -1135,32 +1461,7 @@ ast_function_expression::hir(exec_list *instructions, * constant representing the complete collection of parameters. */ if (all_parameters_are_constant) { - if (components_used >= type_components) - return new(ctx) ir_constant(constructor_type, - & actual_parameters); - - /* The above case must handle all scalar constructors. - */ - assert(constructor_type->is_vector() - || constructor_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 (constructor_type->is_matrix()) - generate_constructor_matrix(constructor_type, initializer, - &data); - else - generate_constructor_vector(constructor_type, initializer, - &data); - - return new(ctx) ir_constant(constructor_type, &data); + return new(ctx) ir_constant(constructor_type, &actual_parameters); } else if (constructor_type->is_scalar()) { return dereference_component((ir_rvalue *) actual_parameters.head, 0); @@ -1178,27 +1479,31 @@ ast_function_expression::hir(exec_list *instructions, } } else { const ast_expression *id = subexpressions[0]; + const char *func_name = id->primary_expression.identifier; 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; + ir_function_signature *sig = + match_function_by_name(func_name, &actual_parameters, state); + + ir_call *call = NULL; + ir_rvalue *value = NULL; + if (sig == NULL) { + no_matching_function_error(func_name, &loc, &actual_parameters, state); + value = ir_rvalue::error_value(ctx); + } else if (!verify_parameter_modes(state, sig, actual_parameters, this->expressions)) { + /* an error has already been emitted */ + value = ir_rvalue::error_value(ctx); + } else { + value = generate_call(instructions, sig, &loc, &actual_parameters, + &call, state); } - return match_function_by_name(instructions, - id->primary_expression.identifier, & loc, - &actual_parameters, state); + return value; } - return ir_call::get_error_instruction(ctx); + return ir_rvalue::error_value(ctx); }