From 471471f83471481db0445e73f8c89e6a9149838e Mon Sep 17 00:00:00 2001 From: Ian Romanick Date: Thu, 11 Mar 2010 14:50:30 -0800 Subject: [PATCH] Initial pass at resolving function calls The code is still really rough and *REALLY* incomplete. This at least passes the first few trivially simple test cases. --- Makefile.am | 2 +- ast_to_hir.cpp | 67 +++++++++++++++++- ir.h | 22 ++++-- ir_function.cpp | 185 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 269 insertions(+), 7 deletions(-) create mode 100644 ir_function.cpp diff --git a/Makefile.am b/Makefile.am index fd0d4f69469..ff4886a9e52 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ bin_PROGRAMS = glsl glsl_SOURCES = symbol_table.c hash_table.c glsl_types.cpp \ glsl_parser.ypp glsl_lexer.lpp glsl_parser_extras.cpp \ ast_expr.cpp ast_to_hir.cpp ir.cpp hir_field_selection.cpp \ - ir_print_visitor.cpp ir_variable.cpp + ir_print_visitor.cpp ir_variable.cpp ir_function.cpp BUILT_SOURCES = glsl_parser.h builtin_types.h diff --git a/ast_to_hir.cpp b/ast_to_hir.cpp index c791aec3e24..d24dc159f21 100644 --- a/ast_to_hir.cpp +++ b/ast_to_hir.cpp @@ -720,11 +720,74 @@ ast_function_expression::hir(exec_list *instructions, * 2. methods - Only the .length() method of array types. * 3. functions - Calls to regular old functions. * + * 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. + * * Method calls are actually detected when the ast_field_selection * expression is handled. */ - (void) instructions; - (void) state; + if (is_constructor()) { + return ir_call::get_error_instruction(); + } else { + const ast_expression *id = subexpressions[0]; + + ir_function *f = (ir_function *) + _mesa_symbol_table_find_symbol(state->symbols, 0, + id->primary_expression.identifier); + + if (f == NULL) { + YYLTYPE loc = id->get_location(); + + _mesa_glsl_error(& loc, state, "function `%s' undeclared", + id->primary_expression.identifier); + return ir_call::get_error_instruction(); + } + + /* Once we've determined that the function being called might exist, + * process the parameters. + */ + exec_list actual_parameters; + simple_node *const first = subexpressions[1]; + if (first != NULL) { + simple_node *ptr = first; + do { + ir_instruction *const result = + ((ast_node *) ptr)->hir(instructions, state); + ptr = ptr->next; + + actual_parameters.push_tail(result); + } while (ptr != first); + } + + /* After processing the function's actual parameters, try to find an + * overload of the function that matches. + */ + const ir_function_signature *sig = + f->matching_signature(& actual_parameters); + if (sig != NULL) { + /* FINISHME: The list of actual parameters needs to be modified to + * FINISHME: include any necessary conversions. + */ + return new ir_call(sig, & actual_parameters); + } else { + YYLTYPE loc = id->get_location(); + + /* 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'", + id->primary_expression.identifier); + return ir_call::get_error_instruction(); + } + } + return ir_call::get_error_instruction(); } diff --git a/ir.h b/ir.h index 136b45b72be..5acf611e746 100644 --- a/ir.h +++ b/ir.h @@ -160,11 +160,19 @@ public: v->visit(this); } + /** + * Find a signature that matches a set of actual parameters. + */ + const ir_function_signature *matching_signature(exec_list *actual_param); + /** * Name of the function. */ const char *name; + /** + * Set of overloaded functions with this name. + */ struct exec_list signatures; }; /*@}*/ @@ -283,10 +291,10 @@ public: */ class ir_call : public ir_instruction { public: - ir_call() - : ir_instruction(ir_op_call), callee(NULL) + ir_call(const ir_function_signature *callee, exec_list *actual_parameters) + : ir_instruction(ir_op_call), callee(callee) { - /* empty */ + actual_parameters->move_nodes_to(& this->actual_parameters); } virtual void accept(ir_visitor *v) @@ -300,7 +308,13 @@ public: static ir_call *get_error_instruction(); private: - ir_function_signature *callee; + ir_call() + : ir_instruction(ir_op_call), callee(NULL) + { + /* empty */ + } + + const ir_function_signature *callee; exec_list actual_parameters; }; diff --git a/ir_function.cpp b/ir_function.cpp new file mode 100644 index 00000000000..a14b546bc6e --- /dev/null +++ b/ir_function.cpp @@ -0,0 +1,185 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "glsl_types.h" +#include "ir.h" + +int +type_compare(const glsl_type *a, const glsl_type *b) +{ + switch (a->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + if ((a->vector_elements != b->vector_elements) + || (a->matrix_rows != b->matrix_rows)) + return -1; + + if (a->base_type == b->base_type) + return 0; + + /* There is no implicit conversion to or from bool. + */ + if ((a->base_type == GLSL_TYPE_BOOL) + || (b->base_type == GLSL_TYPE_BOOL)) + return -1; + + return 1; + + case GLSL_TYPE_SAMPLER: + return ((a->sampler_dimensionality == b->sampler_dimensionality) + && (a->sampler_shadow == b->sampler_shadow) + && (a->sampler_array == b->sampler_array) + && (a->sampler_type == b->sampler_type)) + ? 0 : -1; + + case GLSL_TYPE_STRUCT: + return (strcmp(a->name, b->name) == 0) ? 0 : -1; + + case GLSL_TYPE_ARRAY: + if ((b->base_type != GLSL_TYPE_ARRAY) + || (a->length != b->length)) + return -1; + + /* From GLSL 1.50 spec, page 27 (page 33 of the PDF): + * "There are no implicit array or structure conversions." + * + * If the comparison of the array element types detects that a conversion + * would be required, the array types do not match. + */ + return (type_compare(a->fields.array, b->fields.array) == 0) ? 0 : -1; + + case GLSL_TYPE_FUNCTION: + case GLSL_TYPE_VOID: + case GLSL_TYPE_ERROR: + default: + /* These are all error conditions. It is invalid for a parameter to + * a function to be declared as error, void, or a function. + */ + return -1; + } + + /* This point should be unreachable. + */ + assert(0); +} + + +static int +parameter_lists_match(exec_list *list_a, exec_list *list_b) +{ + exec_list_iterator iter_a = list_a->iterator(); + exec_list_iterator iter_b = list_b->iterator(); + int total_score = 0; + + for (/* empty */ ; iter_a.has_next(); iter_a.next(), iter_b.next()) { + /* If all of the parameters from the other parameter list have been + * exhausted, the lists have different length and, by definition, + * do not match. + */ + if (!iter_b.has_next()) + return -1; + + + const ir_variable *const param = (ir_variable *) iter_a.get(); + const ir_instruction *const actual = (ir_instruction *) iter_b.get(); + + /* Determine whether or not the types match. If the types are an + * exact match, the match score is zero. If the types don't match + * but the actual parameter can be coerced to the type of the declared + * parameter, the match score is one. + */ + int score; + switch ((enum ir_variable_mode)(param->mode)) { + case ir_var_auto: + case ir_var_uniform: + /* These are all error conditions. It is invalid for a parameter to + * a function to be declared as auto (not in, out, or inout) or + * as uniform. + */ + assert(0); + return -1; + + case ir_var_in: + score = type_compare(param->type, actual->type); + break; + + case ir_var_out: + /* FINISHME: Make sure that actual is a valid lvalue. */ + score = type_compare(actual->type, param->type); + break; + + case ir_var_inout: + /* FINISHME: Make sure that actual is a valid lvalue. */ + + /* Since there are no bi-directional automatic conversions (e.g., + * there is int -> float but no float -> int), inout parameters must + * be exact matches. + */ + score = (type_compare(actual->type, param->type) == 0) ? 0 : -1; + break; + } + + if (score < 0) + return -1; + + total_score += score; + } + + /* If all of the parameters from the other parameter list have been + * exhausted, the lists have different length and, by definition, do not + * match. + */ + if (iter_b.has_next()) + return -1; + + return total_score; +} + + +const ir_function_signature * +ir_function::matching_signature(exec_list *actual_parameters) +{ + ir_function_signature *match = NULL; + + foreach_iter(exec_list_iterator, iter, signatures) { + ir_function_signature *const sig = + (ir_function_signature *) iter.get(); + + const int score = parameter_lists_match(& sig->parameters, + actual_parameters); + + if (score == 0) + return sig; + + if (score > 0) { + if (match != NULL) + return NULL; + + match = sig; + } + } + + return match; +} -- 2.30.2