X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fglsl%2Fast_function.cpp;h=cbff9d8b452ba9fab7afda1f8cc6ab12fe03af3b;hb=c5be9c126d6ca9380cd381a5eb22554e4bb71a64;hp=6ecf779c935abaf7a8ef7741f7ae5387f30b6a59;hpb=652901e95b4ed406293d0e1fabee857c054119b1;p=mesa.git diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp index 6ecf779c935..cbff9d8b452 100644 --- a/src/glsl/ast_function.cpp +++ b/src/glsl/ast_function.cpp @@ -41,8 +41,7 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters, { unsigned count = 0; - foreach_list (n, parameters) { - ast_node *const ast = exec_node_data(ast_node, n, link); + foreach_list_typed(ast_node, ast, link, parameters) { ir_rvalue *result = ast->hir(instructions, state); ir_constant *const constant = result->constant_expression_value(); @@ -62,11 +61,13 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters, * * \param return_type Return type of the function. May be \c NULL. * \param name Name of the function. - * \param parameters Parameter list for the function. This may be either a - * formal or actual parameter list. Only the type is used. + * \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 talloced string representing the prototype of the function. + * A ralloced string representing the prototype of the function. */ char * prototype_string(const glsl_type *return_type, const char *name, @@ -75,153 +76,466 @@ prototype_string(const glsl_type *return_type, const char *name, char *str = NULL; if (return_type != NULL) - str = talloc_asprintf(str, "%s ", return_type->name); + str = ralloc_asprintf(NULL, "%s ", return_type->name); - str = talloc_asprintf_append(str, "%s(", name); + ralloc_asprintf_append(&str, "%s(", name); const char *comma = ""; - foreach_list(node, parameters) { - const ir_instruction *const param = (ir_instruction *) node; - - str = talloc_asprintf_append(str, "%s%s", comma, param->type->name); + foreach_in_list(const ir_variable, param, parameters) { + ralloc_asprintf_append(&str, "%s%s", comma, param->type->name); comma = ", "; } - str = talloc_strdup_append(str, ")"); + ralloc_strcat(&str, ")"); return str; } +static bool +verify_image_parameter(YYLTYPE *loc, _mesa_glsl_parse_state *state, + const ir_variable *formal, const ir_variable *actual) +{ + /** + * From the ARB_shader_image_load_store specification: + * + * "The values of image variables qualified with coherent, + * volatile, restrict, readonly, or writeonly may not be passed + * to functions whose formal parameters lack such + * qualifiers. [...] It is legal to have additional qualifiers + * on a formal parameter, but not to have fewer." + */ + if (actual->data.image_coherent && !formal->data.image_coherent) { + _mesa_glsl_error(loc, state, + "function call parameter `%s' drops " + "`coherent' qualifier", formal->name); + return false; + } -static ir_rvalue * -match_function_by_name(exec_list *instructions, const char *name, - YYLTYPE *loc, exec_list *actual_parameters, - struct _mesa_glsl_parse_state *state) + if (actual->data.image_volatile && !formal->data.image_volatile) { + _mesa_glsl_error(loc, state, + "function call parameter `%s' drops " + "`volatile' qualifier", formal->name); + return false; + } + + if (actual->data.image_restrict && !formal->data.image_restrict) { + _mesa_glsl_error(loc, state, + "function call parameter `%s' drops " + "`restrict' qualifier", formal->name); + return false; + } + + if (actual->data.image_read_only && !formal->data.image_read_only) { + _mesa_glsl_error(loc, state, + "function call parameter `%s' drops " + "`readonly' qualifier", formal->name); + return false; + } + + if (actual->data.image_write_only && !formal->data.image_write_only) { + _mesa_glsl_error(loc, state, + "function call parameter `%s' drops " + "`writeonly' qualifier", formal->name); + return false; + } + + return true; +} + +/** + * 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) { - void *ctx = state; - ir_function *f = state->symbols->get_function(name); - ir_function_signature *sig; + exec_node *actual_ir_node = actual_ir_parameters.head; + exec_node *actual_ast_node = actual_ast_parameters.head; - sig = f ? f->matching_signature(actual_parameters) : NULL; + foreach_in_list(const ir_variable, formal, &sig->parameters) { + /* The lists must be the same length. */ + assert(!actual_ir_node->is_tail_sentinel()); + assert(!actual_ast_node->is_tail_sentinel()); - /* FINISHME: This doesn't handle the case where shader X contains a - * FINISHME: matching signature but shader X + N contains an _exact_ - * FINISHME: matching signature. - */ - if (sig == NULL && (f == NULL || state->es_shader || !f->has_user_signature()) && state->symbols->get_type(name) == NULL && (state->language_version == 110 || state->symbols->get_variable(name) == NULL)) { - /* The current shader doesn't contain a matching function or signature. - * Before giving up, look for the prototype in the built-in functions. + 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). */ - for (unsigned i = 0; i < state->num_builtins_to_link; i++) { - ir_function *builtin; - builtin = state->builtins_to_link[i]->symbols->get_function(name); - sig = builtin ? builtin->matching_signature(actual_parameters) : NULL; - if (sig != NULL) { - if (f == NULL) { - f = new(ctx) ir_function(name); - state->symbols->add_global_function(f); - emit_function(state, instructions, f); - } + YYLTYPE loc = actual_ast->get_location(); + + /* Verify that 'const_in' parameters are ir_constants. */ + if (formal->data.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; + } - f->add_signature(sig->clone_prototype(f, NULL)); - break; + /* Verify that shader_in parameters are shader inputs */ + if (formal->data.must_be_shader_input) { + ir_variable *var = actual->variable_referenced(); + if (var && var->data.mode != ir_var_shader_in) { + _mesa_glsl_error(&loc, state, + "parameter `%s` must be a shader input", + formal->name); + return false; + } + + if (actual->ir_type == ir_type_swizzle) { + _mesa_glsl_error(&loc, state, + "parameter `%s` must not be swizzled", + formal->name); + return false; + } + } + + /* Verify that 'out' and 'inout' actual parameters are lvalues. */ + if (formal->data.mode == ir_var_function_out + || formal->data.mode == ir_var_function_inout) { + const char *mode = NULL; + switch (formal->data.mode) { + case ir_var_function_out: mode = "out"; break; + case ir_var_function_inout: mode = "inout"; break; + default: assert(false); break; + } + + /* 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; } + + ir_variable *var = actual->variable_referenced(); + if (var) + var->data.assigned = true; + + if (var && var->data.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()) { + /* Even though ir_binop_vector_extract is not an l-value, let it + * slop through. generate_call will handle it correctly. + */ + ir_expression *const expr = ((ir_rvalue *) actual)->as_expression(); + if (expr == NULL + || expr->operation != ir_binop_vector_extract + || !expr->operands[0]->is_lvalue()) { + _mesa_glsl_error(&loc, state, + "function parameter '%s %s' is not an lvalue", + mode, formal->name); + return false; + } + } + } + + if (formal->type->is_image() && + actual->variable_referenced()) { + if (!verify_image_parameter(&loc, state, formal, + actual->variable_referenced())) + return false; } + + actual_ir_node = actual_ir_node->next; + actual_ast_node = actual_ast_node->next; } + return true; +} - 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. +static void +fix_parameter(void *mem_ctx, ir_rvalue *actual, const glsl_type *formal_type, + exec_list *before_instructions, exec_list *after_instructions, + bool parameter_is_inout) +{ + ir_expression *const expr = actual->as_expression(); + + /* If the types match exactly and the parameter is not a vector-extract, + * nothing needs to be done to fix the parameter. + */ + if (formal_type == actual->type + && (expr == NULL || expr->operation != ir_binop_vector_extract)) + return; + + /* 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); + * + * If the parameter is an ir_expression of ir_binop_vector_extract, + * additional conversion is needed in the post-call re-write. + */ + ir_variable *tmp = + new(mem_ctx) ir_variable(formal_type, "inout_tmp", ir_var_temporary); + + before_instructions->push_tail(tmp); + + /* If the parameter is an inout parameter, copy the value of the actual + * parameter to the new temporary. Note that no type conversion is allowed + * here because inout parameters must match types exactly. + */ + if (parameter_is_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. */ - 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_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"); - } - } + assert (actual->type == formal_type); + + ir_dereference_variable *const deref_tmp_1 = + new(mem_ctx) ir_dereference_variable(tmp); + ir_assignment *const assignment = + new(mem_ctx) ir_assignment(deref_tmp_1, actual); + before_instructions->push_tail(assignment); + } + + /* Replace the parameter in the call with a dereference of the new + * temporary. + */ + ir_dereference_variable *const deref_tmp_2 = + new(mem_ctx) ir_dereference_variable(tmp); + actual->replace_with(deref_tmp_2); + + + /* Copy the temporary variable to the actual parameter with optional + * type conversion applied. + */ + ir_rvalue *rhs = new(mem_ctx) ir_dereference_variable(tmp); + if (actual->type != formal_type) + rhs = convert_component(rhs, actual->type); + + ir_rvalue *lhs = actual; + if (expr != NULL && expr->operation == ir_binop_vector_extract) { + rhs = new(mem_ctx) ir_expression(ir_triop_vector_insert, + expr->operands[0]->type, + expr->operands[0]->clone(mem_ctx, NULL), + rhs, + expr->operands[1]->clone(mem_ctx, NULL)); + lhs = expr->operands[0]->clone(mem_ctx, NULL); + } - if (formal->type->is_numeric() || formal->type->is_boolean()) { - ir_rvalue *converted = convert_component(actual, formal->type); + ir_assignment *const assignment_2 = new(mem_ctx) ir_assignment(lhs, rhs); + after_instructions->push_tail(assignment_2); +} + +/** + * Generate a function call. + * + * For non-void functions, this returns a dereference of the temporary variable + * which stores the return value for the call. For void functions, this returns + * NULL. + */ +static ir_rvalue * +generate_call(exec_list *instructions, ir_function_signature *sig, + exec_list *actual_parameters, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + exec_list post_call_conversions; + + /* 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. + */ + foreach_two_lists(formal_node, &sig->parameters, + actual_node, actual_parameters) { + ir_rvalue *actual = (ir_rvalue *) actual_node; + ir_variable *formal = (ir_variable *) formal_node; + + if (formal->type->is_numeric() || formal->type->is_boolean()) { + switch (formal->data.mode) { + case ir_var_const_in: + case ir_var_function_in: { + ir_rvalue *converted + = convert_component(actual, formal->type); actual->replace_with(converted); + break; + } + case ir_var_function_out: + case ir_var_function_inout: + fix_parameter(ctx, actual, formal->type, + instructions, &post_call_conversions, + formal->data.mode == ir_var_function_inout); + 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; + /* 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 and GLSL ES 3.00. + */ + if (state->is_version(120, 300)) { + ir_constant *value = sig->constant_expression_value(actual_parameters, NULL); + if (value != NULL) { + return value; } - } else { - char *str = prototype_string(NULL, name, actual_parameters); + } - _mesa_glsl_error(loc, state, "no matching function for call to `%s'", - str); - talloc_free(str); + ir_dereference_variable *deref = NULL; + if (!sig->return_type->is_void()) { + /* Create a new temporary to hold the return value. */ + char *const name = ir_variable::temporaries_allocate_names + ? ralloc_asprintf(ctx, "%s_retval", sig->function_name()) + : NULL; - const char *prefix = "candidates are: "; + ir_variable *var; - for (int i = -1; i < state->num_builtins_to_link; i++) { - glsl_symbol_table *syms = i >= 0 ? state->builtins_to_link[i]->symbols - : state->symbols; - f = syms->get_function(name); - if (f == NULL) - continue; + var = new(ctx) ir_variable(sig->return_type, name, ir_var_temporary); + instructions->push_tail(var); - foreach_list (node, &f->signatures) { - ir_function_signature *sig = (ir_function_signature *) node; + ralloc_free(name); - str = prototype_string(sig->return_type, f->name, &sig->parameters); - _mesa_glsl_error(loc, state, "%s%s\n", prefix, str); - talloc_free(str); + deref = new(ctx) ir_dereference_variable(var); + } + ir_call *call = new(ctx) ir_call(sig, deref, actual_parameters); + instructions->push_tail(call); - prefix = " "; - } + /* 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->symbols->separate_function_namespace + && state->symbols->get_variable(name)) + goto done; /* no match */ + + if (f != NULL) { + /* 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 consider them. + */ + bool allow_builtins = state->es_shader || !f->has_user_signature(); + + /* Look for a match in the local shader. If exact, we're done. */ + bool is_exact = false; + sig = local_sig = f->matching_signature(state, actual_parameters, + allow_builtins, &is_exact); + if (is_exact) + goto done; + + if (!allow_builtins) + goto done; + } + + /* Local shader has no exact candidates; check the built-ins. */ + _mesa_glsl_initialize_builtin_functions(); + sig = _mesa_glsl_find_builtin_function(state, name, actual_parameters); + +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; +} + +static void +print_function_prototypes(_mesa_glsl_parse_state *state, YYLTYPE *loc, + ir_function *f) +{ + if (f == NULL) + return; - return ir_call::get_error_instruction(ctx); + foreach_in_list(ir_function_signature, sig, &f->signatures) { + if (sig->is_builtin() && !sig->is_builtin_available(state)) + continue; + + char *str = prototype_string(sig->return_type, f->name, &sig->parameters); + _mesa_glsl_error(loc, state, " %s", str); + ralloc_free(str); } } +/** + * 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) +{ + gl_shader *sh = _mesa_glsl_get_builtin_function_shader(); + + if (state->symbols->get_function(name) == NULL + && (!state->uses_builtin_functions + || sh->symbols->get_function(name) == NULL)) { + _mesa_glsl_error(loc, state, "no function with name '%s'", name); + } else { + char *str = prototype_string(NULL, name, actual_parameters); + _mesa_glsl_error(loc, state, + "no matching function for call to `%s'; candidates are:", + str); + ralloc_free(str); + + print_function_prototypes(state, loc, state->symbols->get_function(name)); + + if (state->uses_builtin_functions) { + print_function_prototypes(state, loc, sh->symbols->get_function(name)); + } + } +} /** * Perform automatic type conversion of constructor parameters @@ -232,7 +546,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; @@ -243,17 +557,35 @@ 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_f2u, 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: @@ -272,6 +604,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; @@ -283,6 +618,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(); @@ -295,7 +631,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 @@ -330,6 +666,129 @@ dereference_component(ir_rvalue *src, unsigned component) } +static ir_rvalue * +process_vec_mat_constructor(exec_list *instructions, + const glsl_type *constructor_type, + YYLTYPE *loc, exec_list *parameters, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + /* The ARB_shading_language_420pack spec says: + * + * "If an initializer is a list of initializers enclosed in curly braces, + * the variable being declared must be a vector, a matrix, an array, or a + * structure. + * + * int i = { 1 }; // illegal, i is not an aggregate" + */ + if (constructor_type->vector_elements <= 1) { + _mesa_glsl_error(loc, state, "aggregates can only initialize vectors, " + "matrices, arrays, and structs"); + return ir_rvalue::error_value(ctx); + } + + exec_list actual_parameters; + const unsigned parameter_count = + process_parameters(instructions, &actual_parameters, parameters, state); + + if (parameter_count == 0 + || (constructor_type->is_vector() && + constructor_type->vector_elements != parameter_count) + || (constructor_type->is_matrix() && + constructor_type->matrix_columns != parameter_count)) { + _mesa_glsl_error(loc, state, "%s constructor must have %u parameters", + constructor_type->is_vector() ? "vector" : "matrix", + constructor_type->vector_elements); + return ir_rvalue::error_value(ctx); + } + + bool all_parameters_are_constant = true; + + /* Type cast each parameter and, if possible, fold constants. */ + foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) { + ir_rvalue *result = ir; + + /* Apply implicit conversions (not the scalar constructor rules!). See + * the spec quote above. */ + if (constructor_type->is_float()) { + const glsl_type *desired_type = + glsl_type::get_instance(GLSL_TYPE_FLOAT, + ir->type->vector_elements, + ir->type->matrix_columns); + if (result->type->can_implicitly_convert_to(desired_type, state)) { + /* 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 (constructor_type->is_matrix()) { + if (result->type != constructor_type->column_type()) { + _mesa_glsl_error(loc, state, "type error in matrix constructor: " + "expected: %s, found %s", + constructor_type->column_type()->name, + result->type->name); + return ir_rvalue::error_value(ctx); + } + } else if (result->type != constructor_type->get_scalar_type()) { + _mesa_glsl_error(loc, state, "type error in vector constructor: " + "expected: %s, found %s", + constructor_type->get_scalar_type()->name, + result->type->name); + return ir_rvalue::error_value(ctx); + } + + /* Attempt to convert the parameter to a constant valued expression. + * After doing so, track whether or not all the parameters to the + * constructor are trivially constant valued expressions. + */ + ir_rvalue *const constant = result->constant_expression_value(); + + if (constant != NULL) + result = constant; + else + all_parameters_are_constant = false; + + ir->replace_with(result); + } + + if (all_parameters_are_constant) + return new(ctx) ir_constant(constructor_type, &actual_parameters); + + ir_variable *var = new(ctx) ir_variable(constructor_type, "vec_mat_ctor", + ir_var_temporary); + instructions->push_tail(var); + + int i = 0; + + foreach_in_list(ir_rvalue, rhs, &actual_parameters) { + ir_instruction *assignment = NULL; + + if (var->type->is_matrix()) { + ir_rvalue *lhs = new(ctx) ir_dereference_array(var, + new(ctx) ir_constant(i)); + assignment = new(ctx) ir_assignment(lhs, rhs, NULL); + } else { + /* use writemask rather than index for vector */ + assert(var->type->is_vector()); + assert(i < 4); + ir_dereference *lhs = new(ctx) ir_dereference_variable(var); + assignment = new(ctx) ir_assignment(lhs, rhs, NULL, (unsigned)(1 << i)); + } + + instructions->push_tail(assignment); + + i++; + } + + return new(ctx) ir_dereference_variable(var); +} + + static ir_rvalue * process_array_constructor(exec_list *instructions, const glsl_type *constructor_type, @@ -360,21 +819,21 @@ process_array_constructor(exec_list *instructions, exec_list actual_parameters; const unsigned parameter_count = process_parameters(instructions, &actual_parameters, parameters, state); + bool is_unsized_array = constructor_type->is_unsized_array(); - if ((parameter_count == 0) - || ((constructor_type->length != 0) - && (constructor_type->length != parameter_count))) { - const unsigned min_param = (constructor_type->length == 0) - ? 1 : constructor_type->length; + if ((parameter_count == 0) || + (!is_unsized_array && (constructor_type->length != parameter_count))) { + const unsigned min_param = is_unsized_array + ? 1 : constructor_type->length; _mesa_glsl_error(loc, state, "array constructor must have %s %u " "parameter%s", - (constructor_type->length != 0) ? "at least" : "exactly", + is_unsized_array ? "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) { + if (is_unsized_array) { constructor_type = glsl_type::get_array_instance(constructor_type->element_type(), parameter_count); @@ -385,17 +844,24 @@ process_array_constructor(exec_list *instructions, bool all_parameters_are_constant = true; /* Type cast each parameter and, if possible, fold constants. */ - foreach_list_safe(n, &actual_parameters) { - ir_rvalue *ir = (ir_rvalue *) n; + foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) { 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, state)) { + /* 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()) { @@ -403,6 +869,7 @@ process_array_constructor(exec_list *instructions, "expected: %s, found %s", constructor_type->element_type()->name, result->type->name); + return ir_rvalue::error_value(ctx); } /* Attempt to convert the parameter to a constant valued expression. @@ -427,8 +894,7 @@ process_array_constructor(exec_list *instructions, instructions->push_tail(var); int i = 0; - foreach_list(node, &actual_parameters) { - ir_rvalue *rhs = (ir_rvalue *) node; + foreach_in_list(ir_rvalue, rhs, &actual_parameters) { ir_rvalue *lhs = new(ctx) ir_dereference_array(var, new(ctx) ir_constant(i)); @@ -449,8 +915,8 @@ static ir_constant * constant_record_constructor(const glsl_type *constructor_type, exec_list *parameters, void *mem_ctx) { - foreach_list(node, parameters) { - ir_constant *constant = ((ir_instruction *) node)->as_constant(); + foreach_in_list(ir_instruction, node, parameters) { + ir_constant *constant = node->as_constant(); if (constant == NULL) return NULL; node->replace_with(constant); @@ -524,8 +990,7 @@ emit_inline_vector_constructor(const glsl_type *type, memset(&data, 0, sizeof(data)); - foreach_list(node, parameters) { - ir_rvalue *param = (ir_rvalue *) node; + foreach_in_list(ir_rvalue, param, parameters) { unsigned rhs_components = param->type->components(); /* Do not try to assign more components to the vector than it has! @@ -582,8 +1047,7 @@ emit_inline_vector_constructor(const glsl_type *type, } base_component = 0; - foreach_list(node, parameters) { - ir_rvalue *param = (ir_rvalue *) node; + foreach_in_list(ir_rvalue, param, parameters) { unsigned rhs_components = param->type->components(); /* Do not try to assign more components to the vector than it has! @@ -869,8 +1333,7 @@ emit_inline_matrix_constructor(const glsl_type *type, unsigned col_idx = 0; unsigned row_idx = 0; - foreach_list (node, parameters) { - ir_rvalue *const rhs = (ir_rvalue *) node; + foreach_in_list(ir_rvalue, rhs, parameters) { const unsigned components_remaining_this_column = rows - row_idx; unsigned rhs_components = rhs->type->components(); unsigned rhs_base = 0; @@ -972,6 +1435,63 @@ emit_inline_record_constructor(const glsl_type *type, } +static ir_rvalue * +process_record_constructor(exec_list *instructions, + const glsl_type *constructor_type, + YYLTYPE *loc, exec_list *parameters, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + exec_list actual_parameters; + + process_parameters(instructions, &actual_parameters, + parameters, 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); +} + + ir_rvalue * ast_function_expression::hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) @@ -993,20 +1513,29 @@ 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); + if (!state->check_version(120, 300, &loc, + "array constructors forbidden")) { + return ir_rvalue::error_value(ctx); } return process_array_constructor(instructions, constructor_type, @@ -1014,16 +1543,28 @@ ast_function_expression::hir(exec_list *instructions, } - /* 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 - * correct scalar type. Constructors for arrays and structures must - * have the exact number of parameters with matching types in the - * correct order. These constructors follow essentially the same type - * matching rules as functions. + /* There are two kinds of constructor calls. Constructors for arrays and + * structures must have the exact number of arguments with matching types + * in the correct order. These constructors follow essentially the same + * type matching rules as functions. + * + * Constructors for built-in language types, such as mat4 and vec2, are + * free form. The only requirements are that the parameters must provide + * enough values of the correct scalar type and that no arguments are + * given past the last used argument. + * + * When using the C-style initializer syntax from GLSL 4.20, constructors + * must have the exact number of arguments with matching types in the + * correct order. */ + if (constructor_type->is_record()) { + return process_record_constructor(instructions, constructor_type, + &loc, &this->expressions, + 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(); @@ -1037,9 +1578,8 @@ ast_function_expression::hir(exec_list *instructions, unsigned nonmatrix_parameters = 0; exec_list actual_parameters; - foreach_list (n, &this->expressions) { - ast_node *ast = exec_node_data(ast_node, n, link); - ir_rvalue *result = ast->hir(instructions, state)->as_rvalue(); + foreach_list_typed(ast_node, ast, link, &this->expressions) { + ir_rvalue *result = ast->hir(instructions, state); /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: * @@ -1050,14 +1590,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 @@ -1077,12 +1617,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 - && 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); + if (matrix_parameters > 0 + && constructor_type->is_matrix() + && !state->check_version(120, 100, &loc, + "cannot construct `%s' from a matrix", + constructor_type->name)) { + return ir_rvalue::error_value(ctx); } /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: @@ -1096,7 +1636,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: @@ -1110,7 +1650,7 @@ ast_function_expression::hir(exec_list *instructions, _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 @@ -1118,9 +1658,7 @@ ast_function_expression::hir(exec_list *instructions, * need to break them up into a series of column vectors. */ if (constructor_type->base_type != GLSL_TYPE_FLOAT) { - foreach_list_safe(n, &actual_parameters) { - ir_rvalue *matrix = (ir_rvalue *) n; - + foreach_in_list_safe(ir_rvalue, matrix, &actual_parameters) { if (!matrix->type->is_matrix()) continue; @@ -1144,9 +1682,7 @@ ast_function_expression::hir(exec_list *instructions, bool all_parameters_are_constant = true; /* Type cast each parameter and, if possible, fold constants.*/ - foreach_list_safe(n, &actual_parameters) { - ir_rvalue *ir = (ir_rvalue *) n; - + foreach_in_list_safe(ir_rvalue, ir, &actual_parameters) { const glsl_type *desired_type = glsl_type::get_instance(constructor_type->base_type, ir->type->vector_elements, @@ -1191,64 +1727,62 @@ ast_function_expression::hir(exec_list *instructions, } } else { const ast_expression *id = subexpressions[0]; - YYLTYPE loc = id->get_location(); + const char *func_name = id->primary_expression.identifier; + YYLTYPE loc = 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); + ir_function_signature *sig = + match_function_by_name(func_name, &actual_parameters, state); - if ((type != NULL) && type->is_record()) { - exec_node *node = actual_parameters.head; - for (unsigned i = 0; i < type->length; i++) { - ir_rvalue *ir = (ir_rvalue *) node; + 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, &actual_parameters, state); + } - if (node->is_tail_sentinel()) { - _mesa_glsl_error(&loc, state, - "insufficient parameters to constructor " - "for `%s'", - type->name); - return ir_call::get_error_instruction(ctx); - } + return value; + } - if (apply_implicit_conversion(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)", - type->name, - type->fields.structure[i].name, - ir->type->name, - type->fields.structure[i].type->name); - return ir_call::get_error_instruction(ctx);; - } + return ir_rvalue::error_value(ctx); +} - node = node->next; - } +ir_rvalue * +ast_aggregate_initializer::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + YYLTYPE loc = this->get_location(); - if (!node->is_tail_sentinel()) { - _mesa_glsl_error(&loc, state, "too many parameters in constructor " - "for `%s'", type->name); - return ir_call::get_error_instruction(ctx); - } + if (!this->constructor_type) { + _mesa_glsl_error(&loc, state, "type of C-style initializer unknown"); + return ir_rvalue::error_value(ctx); + } + const glsl_type *const constructor_type = this->constructor_type; - ir_rvalue *const constant = - constant_record_constructor(type, &actual_parameters, state); + if (!state->ARB_shading_language_420pack_enable) { + _mesa_glsl_error(&loc, state, "C-style initialization requires the " + "GL_ARB_shading_language_420pack extension"); + return ir_rvalue::error_value(ctx); + } - return (constant != NULL) - ? constant - : emit_inline_record_constructor(type, instructions, - &actual_parameters, state); - } + if (constructor_type->is_array()) { + return process_array_constructor(instructions, constructor_type, &loc, + &this->expressions, state); + } - return match_function_by_name(instructions, - id->primary_expression.identifier, & loc, - &actual_parameters, state); + if (constructor_type->is_record()) { + return process_record_constructor(instructions, constructor_type, &loc, + &this->expressions, state); } - return ir_call::get_error_instruction(ctx); + return process_vec_mat_constructor(instructions, constructor_type, &loc, + &this->expressions, state); }