From dafebc5bb70303f0b5baf0b087cf4d9a64b5c7f0 Mon Sep 17 00:00:00 2001 From: marha Date: Mon, 12 Sep 2011 11:27:51 +0200 Subject: Synchronised line endinge with release branch --- mesalib/src/glsl/ast_expr.cpp | 190 +- mesalib/src/glsl/ast_function.cpp | 2816 ++++---- mesalib/src/glsl/ast_to_hir.cpp | 7234 ++++++++++---------- mesalib/src/glsl/ast_type.cpp | 276 +- mesalib/src/glsl/builtin_types.h | 604 +- mesalib/src/glsl/glcpp/glcpp-parse.y | 3812 +++++------ mesalib/src/glsl/glsl_parser_extras.cpp | 1916 +++--- mesalib/src/glsl/glsl_parser_extras.h | 590 +- mesalib/src/glsl/glsl_symbol_table.cpp | 338 +- mesalib/src/glsl/glsl_types.cpp | 1082 +-- mesalib/src/glsl/hir_field_selection.cpp | 204 +- mesalib/src/glsl/ir.cpp | 3138 ++++----- mesalib/src/glsl/ir.h | 3408 ++++----- mesalib/src/glsl/ir_clone.cpp | 868 +-- mesalib/src/glsl/ir_constant_expression.cpp | 2766 ++++---- mesalib/src/glsl/ir_import_prototypes.cpp | 244 +- mesalib/src/glsl/ir_print_visitor.cpp | 1056 +-- mesalib/src/glsl/ir_print_visitor.h | 188 +- mesalib/src/glsl/ir_validate.cpp | 1244 ++-- mesalib/src/glsl/ir_variable.cpp | 1784 ++--- mesalib/src/glsl/link_functions.cpp | 572 +- mesalib/src/glsl/linker.cpp | 3662 +++++----- mesalib/src/glsl/loop_controls.cpp | 608 +- mesalib/src/glsl/lower_mat_op_to_vec.cpp | 856 +-- mesalib/src/glsl/main.cpp | 584 +- mesalib/src/glsl/opt_constant_propagation.cpp | 890 +-- mesalib/src/glsl/opt_constant_variable.cpp | 390 +- mesalib/src/glsl/opt_copy_propagation_elements.cpp | 934 +-- mesalib/src/glsl/opt_dead_code.cpp | 284 +- mesalib/src/glsl/opt_dead_code_local.cpp | 444 +- mesalib/src/glsl/opt_function_inlining.cpp | 844 +-- mesalib/src/glsl/opt_swizzle_swizzle.cpp | 186 +- mesalib/src/glsl/opt_tree_grafting.cpp | 738 +- mesalib/src/glsl/s_expression.cpp | 422 +- 34 files changed, 22586 insertions(+), 22586 deletions(-) (limited to 'mesalib/src/glsl') diff --git a/mesalib/src/glsl/ast_expr.cpp b/mesalib/src/glsl/ast_expr.cpp index e8812e7a1..e624d11cf 100644 --- a/mesalib/src/glsl/ast_expr.cpp +++ b/mesalib/src/glsl/ast_expr.cpp @@ -1,95 +1,95 @@ -/* - * 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 -#include "ast.h" - -const char * -ast_expression::operator_string(enum ast_operators op) -{ - static const char *const operators[] = { - "=", - "+", - "-", - "+", - "-", - "*", - "/", - "%", - "<<", - ">>", - "<", - ">", - "<=", - ">=", - "==", - "!=", - "&", - "^", - "|", - "~", - "&&", - "^^", - "||", - "!", - - "*=", - "/=", - "%=", - "+=", - "-=", - "<<=", - ">>=", - "&=", - "^=", - "|=", - - "?:", - - "++", - "--", - "++", - "--", - ".", - }; - - assert((unsigned int)op < sizeof(operators) / sizeof(operators[0])); - - return operators[op]; -} - - -ast_expression_bin::ast_expression_bin(int oper, ast_expression *ex0, - ast_expression *ex1) : - ast_expression(oper, ex0, ex1, NULL) -{ - assert((oper >= ast_plus) && (oper <= ast_logic_not)); -} - - -void -ast_expression_bin::print(void) const -{ - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - subexpressions[1]->print(); -} +/* + * 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 +#include "ast.h" + +const char * +ast_expression::operator_string(enum ast_operators op) +{ + static const char *const operators[] = { + "=", + "+", + "-", + "+", + "-", + "*", + "/", + "%", + "<<", + ">>", + "<", + ">", + "<=", + ">=", + "==", + "!=", + "&", + "^", + "|", + "~", + "&&", + "^^", + "||", + "!", + + "*=", + "/=", + "%=", + "+=", + "-=", + "<<=", + ">>=", + "&=", + "^=", + "|=", + + "?:", + + "++", + "--", + "++", + "--", + ".", + }; + + assert((unsigned int)op < sizeof(operators) / sizeof(operators[0])); + + return operators[op]; +} + + +ast_expression_bin::ast_expression_bin(int oper, ast_expression *ex0, + ast_expression *ex1) : + ast_expression(oper, ex0, ex1, NULL) +{ + assert((oper >= ast_plus) && (oper <= ast_logic_not)); +} + + +void +ast_expression_bin::print(void) const +{ + subexpressions[0]->print(); + printf("%s ", operator_string(oper)); + subexpressions[1]->print(); +} diff --git a/mesalib/src/glsl/ast_function.cpp b/mesalib/src/glsl/ast_function.cpp index 397164cde..ca45934a4 100644 --- a/mesalib/src/glsl/ast_function.cpp +++ b/mesalib/src/glsl/ast_function.cpp @@ -1,1408 +1,1408 @@ -/* - * 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_symbol_table.h" -#include "ast.h" -#include "glsl_types.h" -#include "ir.h" -#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, - struct _mesa_glsl_parse_state *state) -{ - unsigned count = 0; - - foreach_list (n, parameters) { - ast_node *const ast = exec_node_data(ast_node, n, link); - ir_rvalue *result = ast->hir(instructions, state); - - ir_constant *const constant = result->constant_expression_value(); - if (constant != NULL) - result = constant; - - actual_parameters->push_tail(result); - count++; - } - - return count; -} - - -/** - * 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) -{ - char *str = NULL; - - if (return_type != NULL) - str = ralloc_asprintf(NULL, "%s ", return_type->name); - - ralloc_asprintf_append(&str, "%s(", name); - - const char *comma = ""; - foreach_list(node, parameters) { - const ir_instruction *const param = (ir_instruction *) node; - - ralloc_asprintf_append(&str, "%s%s", comma, param->type->name); - comma = ", "; - } - - ralloc_strcat(&str, ")"); - return str; -} - - -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) -{ - void *ctx = state; - ir_function *f = state->symbols->get_function(name); - ir_function_signature *sig; - - sig = f ? f->matching_signature(actual_parameters) : NULL; - - /* 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. - */ - 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, f); - } - - f->add_signature(sig->clone_prototype(f, NULL)); - break; - } - } - } - - exec_list post_call_conversions; - - 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. - * - * Also, validate that 'const_in' formal parameters (an extension of our - * IR) correspond to ir_constant actual parameters. - * - * Also, perform implicit conversion of arguments. Note: to implicitly - * convert 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->mode == ir_var_const_in && !actual->as_constant()) { - _mesa_glsl_error(loc, state, - "parameter `%s' must be a constant expression", - formal->name); - return ir_call::get_error_instruction(ctx); - } - - 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; - } - /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always - * FIXME: 0:0(0). - */ - 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); - - } else if (!actual->is_lvalue()) { - _mesa_glsl_error(loc, state, - "function parameter '%s %s' is not an lvalue", - mode, formal->name); - } - } - - 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. - * - * Also insert any out parameter conversions after the call. - */ - ir_call *call = new(ctx) ir_call(sig, actual_parameters); - ir_dereference_variable *deref; - if (!sig->return_type->is_void()) { - /* If the function call is a constant expression, don't - * generate the instructions to call it; just generate an - * ir_constant representing the constant value. - * - * Function calls can only be constant expressions starting - * in GLSL 1.20. - */ - if (state->language_version >= 120) { - ir_constant *const_val = call->constant_expression_value(); - if (const_val) { - return const_val; - } - } - - ir_variable *var; - - 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_assignment *assign = new(ctx) ir_assignment(deref, call, NULL); - instructions->push_tail(assign); - - deref = new(ctx) ir_dereference_variable(var); - } else { - instructions->push_tail(call); - deref = NULL; - } - instructions->append_list(&post_call_conversions); - return deref; - } else { - 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; - 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 = " "; - } - - } - - return ir_call::get_error_instruction(ctx); - } -} - - -/** - * Perform automatic type conversion of constructor parameters - * - * This implements the rules in the "Conversion and Scalar Constructors" - * section (GLSL 1.10 section 5.4.1), not the "Implicit Conversions" rules. - */ -static ir_rvalue * -convert_component(ir_rvalue *src, const glsl_type *desired_type) -{ - void *ctx = ralloc_parent(src); - const unsigned a = desired_type->base_type; - const unsigned b = src->type->base_type; - ir_expression *result = NULL; - - if (src->type->is_error()) - return src; - - assert(a <= GLSL_TYPE_BOOL); - assert(b <= GLSL_TYPE_BOOL); - - 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: - 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: - switch (b) { - case GLSL_TYPE_UINT: - result = new(ctx) ir_expression(ir_unop_u2f, desired_type, src, NULL); - break; - case GLSL_TYPE_INT: - result = new(ctx) ir_expression(ir_unop_i2f, desired_type, src, NULL); - break; - case GLSL_TYPE_BOOL: - result = new(ctx) ir_expression(ir_unop_b2f, desired_type, src, NULL); - break; - } - break; - 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; - case GLSL_TYPE_FLOAT: - result = new(ctx) ir_expression(ir_unop_f2b, desired_type, src, NULL); - break; - } - break; - } - - 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(); - return (constant != NULL) ? (ir_rvalue *) constant : (ir_rvalue *) result; -} - -/** - * Dereference a specific component from a scalar, vector, or matrix - */ -static ir_rvalue * -dereference_component(ir_rvalue *src, unsigned component) -{ - void *ctx = ralloc_parent(src); - assert(component < src->type->components()); - - /* If the source is a constant, just create a new constant instead of a - * dereference of the existing constant. - */ - ir_constant *constant = src->as_constant(); - if (constant) - return new(ctx) ir_constant(constant, component); - - if (src->type->is_scalar()) { - return src; - } else if (src->type->is_vector()) { - return new(ctx) ir_swizzle(src, component, 0, 0, 0, 1); - } else { - assert(src->type->is_matrix()); - - /* Dereference a row of the matrix, then call this function again to get - * a specific element from that row. - */ - const int c = component / src->type->column_type()->vector_elements; - const int r = component % src->type->column_type()->vector_elements; - ir_constant *const col_index = new(ctx) ir_constant(c); - ir_dereference *const col = new(ctx) ir_dereference_array(src, col_index); - - col->type = src->type->column_type(); - - return dereference_component(col, r); - } - - assert(!"Should not get here."); - return NULL; -} - - -static ir_rvalue * -process_array_constructor(exec_list *instructions, - const glsl_type *constructor_type, - YYLTYPE *loc, exec_list *parameters, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - /* Array constructors come in two forms: sized and unsized. Sized array - * constructors look like 'vec4[2](a, b)', where 'a' and 'b' are vec4 - * variables. In this case the number of parameters must exactly match the - * specified size of the array. - * - * Unsized array constructors look like 'vec4[](a, b)', where 'a' and 'b' - * are vec4 variables. In this case the size of the array being constructed - * is determined by the number of parameters. - * - * From page 52 (page 58 of the PDF) of the GLSL 1.50 spec: - * - * "There must be exactly the same number of arguments as the size of - * the array being constructed. If no size is present in the - * constructor, then the array is explicitly sized to the number of - * arguments provided. The arguments are assigned in order, starting at - * element 0, to the elements of the constructed array. Each argument - * must be the same type as the element type of the array, or be a type - * that can be converted to the element type of the array according to - * Section 4.1.10 "Implicit Conversions."" - */ - exec_list actual_parameters; - const unsigned parameter_count = - process_parameters(instructions, &actual_parameters, parameters, state); - - 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; - - _mesa_glsl_error(loc, state, "array constructor must have %s %u " - "parameter%s", - (constructor_type->length != 0) ? "at least" : "exactly", - min_param, (min_param <= 1) ? "" : "s"); - return ir_call::get_error_instruction(ctx); - } - - if (constructor_type->length == 0) { - constructor_type = - glsl_type::get_array_instance(constructor_type->element_type(), - parameter_count); - assert(constructor_type != NULL); - assert(constructor_type->length == parameter_count); - } - - 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; - ir_rvalue *result = ir; - - /* 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); - 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()) { - _mesa_glsl_error(loc, state, "type error in array constructor: " - "expected: %s, found %s", - constructor_type->element_type()->name, - result->type->name); - } - - /* 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, "array_ctor", - ir_var_temporary); - instructions->push_tail(var); - - int i = 0; - foreach_list(node, &actual_parameters) { - ir_rvalue *rhs = (ir_rvalue *) node; - ir_rvalue *lhs = new(ctx) ir_dereference_array(var, - new(ctx) ir_constant(i)); - - ir_instruction *assignment = new(ctx) ir_assignment(lhs, rhs, NULL); - instructions->push_tail(assignment); - - i++; - } - - return new(ctx) ir_dereference_variable(var); -} - - -/** - * Try to convert a record constructor to a constant expression - */ -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(); - if (constant == NULL) - return NULL; - node->replace_with(constant); - } - - return new(mem_ctx) ir_constant(constructor_type, parameters); -} - - -/** - * Determine if a list consists of a single scalar r-value - */ -bool -single_scalar_parameter(exec_list *parameters) -{ - const ir_rvalue *const p = (ir_rvalue *) parameters->head; - assert(((ir_rvalue *)p)->as_rvalue() != NULL); - - return (p->type->is_scalar() && p->next->is_tail_sentinel()); -} - - -/** - * Generate inline code for a vector constructor - * - * The generated constructor code will consist of a temporary variable - * declaration of the same type as the constructor. A sequence of assignments - * from constructor parameters to the temporary will follow. - * - * \return - * An \c ir_dereference_variable of the temprorary generated in the constructor - * body. - */ -ir_rvalue * -emit_inline_vector_constructor(const glsl_type *type, - exec_list *instructions, - exec_list *parameters, - void *ctx) -{ - assert(!parameters->is_empty()); - - ir_variable *var = new(ctx) ir_variable(type, "vec_ctor", ir_var_temporary); - instructions->push_tail(var); - - /* There are two kinds of vector constructors. - * - * - Construct a vector from a single scalar by replicating that scalar to - * all components of the vector. - * - * - Construct a vector from an arbirary combination of vectors and - * scalars. The components of the constructor parameters are assigned - * to the vector in order until the vector is full. - */ - const unsigned lhs_components = type->components(); - if (single_scalar_parameter(parameters)) { - ir_rvalue *first_param = (ir_rvalue *)parameters->head; - 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, 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 *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_lhs_component) > lhs_components) { - rhs_components = lhs_components - base_lhs_component; - } - - 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. - */ - 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_dereference *lhs = new(ctx) ir_dereference_variable(var); - - /* 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. - */ - base_component += rhs_components; - } - } - return new(ctx) ir_dereference_variable(var); -} - - -/** - * Generate assignment of a portion of a vector to a portion of a matrix column - * - * \param src_base First component of the source to be used in assignment - * \param column Column of destination to be assiged - * \param row_base First component of the destination column to be assigned - * \param count Number of components to be assigned - * - * \note - * \c src_base + \c count must be less than or equal to the number of components - * in the source vector. - */ -ir_instruction * -assign_to_matrix_column(ir_variable *var, unsigned column, unsigned row_base, - ir_rvalue *src, unsigned src_base, unsigned count, - void *mem_ctx) -{ - 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)); - assert(src->type->components() >= (src_base + count)); - - /* 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); -} - - -/** - * Generate inline code for a matrix constructor - * - * The generated constructor code will consist of a temporary variable - * declaration of the same type as the constructor. A sequence of assignments - * from constructor parameters to the temporary will follow. - * - * \return - * An \c ir_dereference_variable of the temprorary generated in the constructor - * body. - */ -ir_rvalue * -emit_inline_matrix_constructor(const glsl_type *type, - exec_list *instructions, - exec_list *parameters, - void *ctx) -{ - assert(!parameters->is_empty()); - - ir_variable *var = new(ctx) ir_variable(type, "mat_ctor", ir_var_temporary); - instructions->push_tail(var); - - /* There are three kinds of matrix constructors. - * - * - Construct a matrix from a single scalar by replicating that scalar to - * along the diagonal of the matrix and setting all other components to - * zero. - * - * - Construct a matrix from an arbirary combination of vectors and - * scalars. The components of the constructor parameters are assigned - * to the matrix in colum-major order until the matrix is full. - * - * - Construct a matrix from a single matrix. The source matrix is copied - * to the upper left portion of the constructed matrix, and the remaining - * elements take values from the identity matrix. - */ - ir_rvalue *const first_param = (ir_rvalue *) parameters->head; - if (single_scalar_parameter(parameters)) { - /* Assign the scalar to the X component of a vec4, and fill the remaining - * components with zero. - */ - ir_variable *rhs_var = - new(ctx) ir_variable(glsl_type::vec4_type, "mat_ctor_vec", - ir_var_temporary); - instructions->push_tail(rhs_var); - - ir_constant_data zero; - zero.f[0] = 0.0; - zero.f[1] = 0.0; - zero.f[2] = 0.0; - zero.f[3] = 0.0; - - ir_instruction *inst = - new(ctx) ir_assignment(new(ctx) ir_dereference_variable(rhs_var), - new(ctx) ir_constant(rhs_var->type, &zero), - NULL); - instructions->push_tail(inst); - - ir_dereference *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); - - 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 - * with a swizzle that puts the X component on the diagonal of the - * matrix. In some cases this may mean that the X component does not - * get assigned into the column at all (i.e., when the matrix has more - * columns than rows). - */ - static const unsigned rhs_swiz[4][4] = { - { 0, 1, 1, 1 }, - { 1, 0, 1, 1 }, - { 1, 1, 0, 1 }, - { 1, 1, 1, 0 } - }; - - 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); - - ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); - ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, rhs_swiz[i], - type->vector_elements); - - inst = new(ctx) ir_assignment(col_ref, rhs, NULL); - instructions->push_tail(inst); - } - - for (unsigned i = cols_to_init; i < type->matrix_columns; i++) { - ir_constant *const col_idx = new(ctx) ir_constant(i); - ir_rvalue *const col_ref = new(ctx) ir_dereference_array(var, col_idx); - - ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); - ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, 1, 1, 1, 1, - type->vector_elements); - - inst = new(ctx) ir_assignment(col_ref, rhs, NULL); - instructions->push_tail(inst); - } - } else if (first_param->type->is_matrix()) { - /* From page 50 (56 of the PDF) of the GLSL 1.50 spec: - * - * "If a matrix is constructed from a matrix, then each component - * (column i, row j) in the result that has a corresponding - * component (column i, row j) in the argument will be initialized - * from there. All other components will be initialized to the - * identity matrix. If a matrix argument is given to a matrix - * constructor, it is an error to have any other arguments." - */ - assert(first_param->next->is_tail_sentinel()); - ir_rvalue *const src_matrix = first_param; - - /* If the source matrix is smaller, pre-initialize the relavent parts of - * the destination matrix to the identity matrix. - */ - if ((src_matrix->type->matrix_columns < var->type->matrix_columns) - || (src_matrix->type->vector_elements < var->type->vector_elements)) { - - /* If the source matrix has fewer rows, every column of the destination - * must be initialized. Otherwise only the columns in the destination - * that do not exist in the source must be initialized. - */ - unsigned col = - (src_matrix->type->vector_elements < var->type->vector_elements) - ? 0 : src_matrix->type->matrix_columns; - - const glsl_type *const col_type = var->type->column_type(); - for (/* empty */; col < var->type->matrix_columns; col++) { - ir_constant_data ident; - - ident.f[0] = 0.0; - ident.f[1] = 0.0; - ident.f[2] = 0.0; - ident.f[3] = 0.0; - - ident.f[col] = 1.0; - - ir_rvalue *const rhs = new(ctx) ir_constant(col_type, &ident); - - ir_rvalue *const lhs = - new(ctx) ir_dereference_array(var, new(ctx) ir_constant(col)); - - ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL); - instructions->push_tail(inst); - } - } - - /* Assign columns from the source matrix to the destination matrix. - * - * Since the parameter will be used in the RHS of multiple assignments, - * generate a temporary and copy the paramter there. - */ - ir_variable *const rhs_var = - new(ctx) ir_variable(first_param->type, "mat_ctor_mat", - ir_var_temporary); - instructions->push_tail(rhs_var); - - ir_dereference *const rhs_var_ref = - new(ctx) ir_dereference_variable(rhs_var); - ir_instruction *const inst = - 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; - - for (unsigned i = 0; i < last_col; i++) { - 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)); - - /* If one matrix has columns that are smaller than the columns of the - * other matrix, wrap the column access of the larger with a swizzle - * so that the LHS and RHS of the assignment have the same size (and - * therefore have the same type). - * - * It would be perfectly valid to unconditionally generate the - * swizzles, this this will typically result in a more compact IR tree. - */ - ir_rvalue *rhs; - if (lhs->type->vector_elements != rhs_col->type->vector_elements) { - rhs = new(ctx) ir_swizzle(rhs_col, swiz, last_row); - } else { - rhs = rhs_col; - } - - ir_instruction *inst = - new(ctx) ir_assignment(lhs, rhs, NULL, write_mask); - instructions->push_tail(inst); - } - } else { - const unsigned cols = type->matrix_columns; - const unsigned rows = type->vector_elements; - unsigned col_idx = 0; - unsigned row_idx = 0; - - foreach_list (node, parameters) { - ir_rvalue *const rhs = (ir_rvalue *) node; - const unsigned components_remaining_this_column = rows - row_idx; - unsigned rhs_components = rhs->type->components(); - unsigned rhs_base = 0; - - /* Since the parameter might be used in the RHS of two assignments, - * generate a temporary and copy the paramter there. - */ - ir_variable *rhs_var = - new(ctx) ir_variable(rhs->type, "mat_ctor_vec", ir_var_temporary); - instructions->push_tail(rhs_var); - - ir_dereference *rhs_var_ref = - new(ctx) ir_dereference_variable(rhs_var); - ir_instruction *inst = new(ctx) ir_assignment(rhs_var_ref, rhs, NULL); - instructions->push_tail(inst); - - /* Assign the current parameter to as many components of the matrix - * as it will fill. - * - * NOTE: A single vector parameter can span two matrix columns. A - * single vec4, for example, can completely fill a mat2. - */ - if (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); - - ir_instruction *inst = assign_to_matrix_column(var, col_idx, - row_idx, - rhs_var_ref, 0, - count, ctx); - instructions->push_tail(inst); - - rhs_base = count; - - col_idx++; - row_idx = 0; - } - - /* If there is data left in the parameter and components left to be - * set in the destination, emit another assignment. It is possible - * that the assignment could be of a vec4 to the last element of the - * matrix. In this case col_idx==cols, but there is still data - * left in the source parameter. Obviously, don't emit an assignment - * to data outside the destination matrix. - */ - if ((col_idx < cols) && (rhs_base < rhs_components)) { - const unsigned count = rhs_components - rhs_base; - - rhs_var_ref = new(ctx) ir_dereference_variable(rhs_var); - - ir_instruction *inst = assign_to_matrix_column(var, col_idx, - row_idx, - rhs_var_ref, - rhs_base, - count, ctx); - instructions->push_tail(inst); - - row_idx += count; - } - } - } - - return new(ctx) ir_dereference_variable(var); -} - - -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) -{ - void *ctx = state; - /* There are three sorts of function calls. - * - * 1. constructors - The first subexpression is an ast_type_specifier. - * 2. methods - Only the .length() method of array types. - * 3. functions - Calls to regular old functions. - * - * Method calls are actually detected when the ast_field_selection - * expression is handled. - */ - if (is_constructor()) { - const ast_type_specifier *type = (ast_type_specifier *) subexpressions[0]; - YYLTYPE loc = type->get_location(); - const char *name; - - 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_call::get_error_instruction(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); - } - - 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 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 - * 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. - */ - 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_call::get_error_instruction(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_call::get_error_instruction(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_call::get_error_instruction(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); - - /* Total number of components of the type being constructed. */ - const unsigned type_components = constructor_type->components(); - - /* Number of components from parameters that have actually been - * consumed. This is used to perform several kinds of error checking. - */ - unsigned components_used = 0; - - unsigned matrix_parameters = 0; - unsigned nonmatrix_parameters = 0; - exec_list actual_parameters; - - foreach_list (n, &this->expressions) { - ast_node *ast = exec_node_data(ast_node, n, link); - ir_rvalue *result = ast->hir(instructions, state)->as_rvalue(); - - /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: - * - * "It is an error to provide extra arguments beyond this - * last used argument." - */ - if (components_used >= type_components) { - _mesa_glsl_error(& loc, state, "too many parameters to `%s' " - "constructor", - constructor_type->name); - return ir_call::get_error_instruction(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); - } - - /* Count the number of matrix and nonmatrix parameters. This - * is used below to enforce some of the constructor rules. - */ - if (result->type->is_matrix()) - matrix_parameters++; - else - nonmatrix_parameters++; - - actual_parameters.push_tail(result); - components_used += result->type->components(); - } - - /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec: - * - * "It is an error to construct matrices from other matrices. This - * is reserved for future use." - */ - if (state->language_version == 110 && matrix_parameters > 0 - && constructor_type->is_matrix()) { - _mesa_glsl_error(& loc, state, "cannot construct `%s' from a " - "matrix in GLSL 1.10", - constructor_type->name); - return ir_call::get_error_instruction(ctx); - } - - /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: - * - * "If a matrix argument is given to a matrix constructor, it is - * an error to have any other arguments." - */ - if ((matrix_parameters > 0) - && ((matrix_parameters + nonmatrix_parameters) > 1) - && constructor_type->is_matrix()) { - _mesa_glsl_error(& loc, state, "for matrix `%s' constructor, " - "matrix must be only parameter", - constructor_type->name); - return ir_call::get_error_instruction(ctx); - } - - /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec: - * - * "In these cases, there must be enough components provided in the - * arguments to provide an initializer for every component in the - * constructed value." - */ - if (components_used < type_components && 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); - } - - /* Later, we cast each parameter to the same base type as the - * constructor. Since there are no non-floating point matrices, we - * 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; - - if (!matrix->type->is_matrix()) - continue; - - /* Create a temporary containing the matrix. */ - ir_variable *var = new(ctx) ir_variable(matrix->type, "matrix_tmp", - ir_var_temporary); - instructions->push_tail(var); - instructions->push_tail(new(ctx) ir_assignment(new(ctx) - ir_dereference_variable(var), matrix, NULL)); - var->constant_value = matrix->constant_expression_value(); - - /* Replace the matrix with dereferences of its columns. */ - for (int i = 0; i < matrix->type->matrix_columns; i++) { - matrix->insert_before(new (ctx) ir_dereference_array(var, - new(ctx) ir_constant(i))); - } - matrix->remove(); - } - } - - 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; - - const glsl_type *desired_type = - glsl_type::get_instance(constructor_type->base_type, - ir->type->vector_elements, - ir->type->matrix_columns); - ir_rvalue *result = convert_component(ir, desired_type); - - /* 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; - - if (result != ir) { - ir->replace_with(result); - } - } - - /* If all of the parameters are trivially constant, create a - * constant representing the complete collection of parameters. - */ - if (all_parameters_are_constant) { - return new(ctx) ir_constant(constructor_type, &actual_parameters); - } else if (constructor_type->is_scalar()) { - return dereference_component((ir_rvalue *) actual_parameters.head, - 0); - } else if (constructor_type->is_vector()) { - return emit_inline_vector_constructor(constructor_type, - instructions, - &actual_parameters, - ctx); - } else { - assert(constructor_type->is_matrix()); - return emit_inline_matrix_constructor(constructor_type, - instructions, - &actual_parameters, - ctx); - } - } else { - const ast_expression *id = subexpressions[0]; - YYLTYPE loc = id->get_location(); - exec_list actual_parameters; - - process_parameters(instructions, &actual_parameters, &this->expressions, - state); - - return match_function_by_name(instructions, - id->primary_expression.identifier, & loc, - &actual_parameters, state); - } - - return ir_call::get_error_instruction(ctx); -} +/* + * 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_symbol_table.h" +#include "ast.h" +#include "glsl_types.h" +#include "ir.h" +#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, + struct _mesa_glsl_parse_state *state) +{ + unsigned count = 0; + + foreach_list (n, parameters) { + ast_node *const ast = exec_node_data(ast_node, n, link); + ir_rvalue *result = ast->hir(instructions, state); + + ir_constant *const constant = result->constant_expression_value(); + if (constant != NULL) + result = constant; + + actual_parameters->push_tail(result); + count++; + } + + return count; +} + + +/** + * 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) +{ + char *str = NULL; + + if (return_type != NULL) + str = ralloc_asprintf(NULL, "%s ", return_type->name); + + ralloc_asprintf_append(&str, "%s(", name); + + const char *comma = ""; + foreach_list(node, parameters) { + const ir_instruction *const param = (ir_instruction *) node; + + ralloc_asprintf_append(&str, "%s%s", comma, param->type->name); + comma = ", "; + } + + ralloc_strcat(&str, ")"); + return str; +} + + +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) +{ + void *ctx = state; + ir_function *f = state->symbols->get_function(name); + ir_function_signature *sig; + + sig = f ? f->matching_signature(actual_parameters) : NULL; + + /* 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. + */ + 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, f); + } + + f->add_signature(sig->clone_prototype(f, NULL)); + break; + } + } + } + + exec_list post_call_conversions; + + 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. + * + * Also, validate that 'const_in' formal parameters (an extension of our + * IR) correspond to ir_constant actual parameters. + * + * Also, perform implicit conversion of arguments. Note: to implicitly + * convert 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->mode == ir_var_const_in && !actual->as_constant()) { + _mesa_glsl_error(loc, state, + "parameter `%s' must be a constant expression", + formal->name); + return ir_call::get_error_instruction(ctx); + } + + 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; + } + /* FIXME: 'loc' is incorrect (as of 2011-01-21). It is always + * FIXME: 0:0(0). + */ + 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); + + } else if (!actual->is_lvalue()) { + _mesa_glsl_error(loc, state, + "function parameter '%s %s' is not an lvalue", + mode, formal->name); + } + } + + 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. + * + * Also insert any out parameter conversions after the call. + */ + ir_call *call = new(ctx) ir_call(sig, actual_parameters); + ir_dereference_variable *deref; + if (!sig->return_type->is_void()) { + /* If the function call is a constant expression, don't + * generate the instructions to call it; just generate an + * ir_constant representing the constant value. + * + * Function calls can only be constant expressions starting + * in GLSL 1.20. + */ + if (state->language_version >= 120) { + ir_constant *const_val = call->constant_expression_value(); + if (const_val) { + return const_val; + } + } + + ir_variable *var; + + 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_assignment *assign = new(ctx) ir_assignment(deref, call, NULL); + instructions->push_tail(assign); + + deref = new(ctx) ir_dereference_variable(var); + } else { + instructions->push_tail(call); + deref = NULL; + } + instructions->append_list(&post_call_conversions); + return deref; + } else { + 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; + 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 = " "; + } + + } + + return ir_call::get_error_instruction(ctx); + } +} + + +/** + * Perform automatic type conversion of constructor parameters + * + * This implements the rules in the "Conversion and Scalar Constructors" + * section (GLSL 1.10 section 5.4.1), not the "Implicit Conversions" rules. + */ +static ir_rvalue * +convert_component(ir_rvalue *src, const glsl_type *desired_type) +{ + void *ctx = ralloc_parent(src); + const unsigned a = desired_type->base_type; + const unsigned b = src->type->base_type; + ir_expression *result = NULL; + + if (src->type->is_error()) + return src; + + assert(a <= GLSL_TYPE_BOOL); + assert(b <= GLSL_TYPE_BOOL); + + 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: + 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: + switch (b) { + case GLSL_TYPE_UINT: + result = new(ctx) ir_expression(ir_unop_u2f, desired_type, src, NULL); + break; + case GLSL_TYPE_INT: + result = new(ctx) ir_expression(ir_unop_i2f, desired_type, src, NULL); + break; + case GLSL_TYPE_BOOL: + result = new(ctx) ir_expression(ir_unop_b2f, desired_type, src, NULL); + break; + } + break; + 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; + case GLSL_TYPE_FLOAT: + result = new(ctx) ir_expression(ir_unop_f2b, desired_type, src, NULL); + break; + } + break; + } + + 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(); + return (constant != NULL) ? (ir_rvalue *) constant : (ir_rvalue *) result; +} + +/** + * Dereference a specific component from a scalar, vector, or matrix + */ +static ir_rvalue * +dereference_component(ir_rvalue *src, unsigned component) +{ + void *ctx = ralloc_parent(src); + assert(component < src->type->components()); + + /* If the source is a constant, just create a new constant instead of a + * dereference of the existing constant. + */ + ir_constant *constant = src->as_constant(); + if (constant) + return new(ctx) ir_constant(constant, component); + + if (src->type->is_scalar()) { + return src; + } else if (src->type->is_vector()) { + return new(ctx) ir_swizzle(src, component, 0, 0, 0, 1); + } else { + assert(src->type->is_matrix()); + + /* Dereference a row of the matrix, then call this function again to get + * a specific element from that row. + */ + const int c = component / src->type->column_type()->vector_elements; + const int r = component % src->type->column_type()->vector_elements; + ir_constant *const col_index = new(ctx) ir_constant(c); + ir_dereference *const col = new(ctx) ir_dereference_array(src, col_index); + + col->type = src->type->column_type(); + + return dereference_component(col, r); + } + + assert(!"Should not get here."); + return NULL; +} + + +static ir_rvalue * +process_array_constructor(exec_list *instructions, + const glsl_type *constructor_type, + YYLTYPE *loc, exec_list *parameters, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + /* Array constructors come in two forms: sized and unsized. Sized array + * constructors look like 'vec4[2](a, b)', where 'a' and 'b' are vec4 + * variables. In this case the number of parameters must exactly match the + * specified size of the array. + * + * Unsized array constructors look like 'vec4[](a, b)', where 'a' and 'b' + * are vec4 variables. In this case the size of the array being constructed + * is determined by the number of parameters. + * + * From page 52 (page 58 of the PDF) of the GLSL 1.50 spec: + * + * "There must be exactly the same number of arguments as the size of + * the array being constructed. If no size is present in the + * constructor, then the array is explicitly sized to the number of + * arguments provided. The arguments are assigned in order, starting at + * element 0, to the elements of the constructed array. Each argument + * must be the same type as the element type of the array, or be a type + * that can be converted to the element type of the array according to + * Section 4.1.10 "Implicit Conversions."" + */ + exec_list actual_parameters; + const unsigned parameter_count = + process_parameters(instructions, &actual_parameters, parameters, state); + + 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; + + _mesa_glsl_error(loc, state, "array constructor must have %s %u " + "parameter%s", + (constructor_type->length != 0) ? "at least" : "exactly", + min_param, (min_param <= 1) ? "" : "s"); + return ir_call::get_error_instruction(ctx); + } + + if (constructor_type->length == 0) { + constructor_type = + glsl_type::get_array_instance(constructor_type->element_type(), + parameter_count); + assert(constructor_type != NULL); + assert(constructor_type->length == parameter_count); + } + + 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; + ir_rvalue *result = ir; + + /* 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); + 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()) { + _mesa_glsl_error(loc, state, "type error in array constructor: " + "expected: %s, found %s", + constructor_type->element_type()->name, + result->type->name); + } + + /* 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, "array_ctor", + ir_var_temporary); + instructions->push_tail(var); + + int i = 0; + foreach_list(node, &actual_parameters) { + ir_rvalue *rhs = (ir_rvalue *) node; + ir_rvalue *lhs = new(ctx) ir_dereference_array(var, + new(ctx) ir_constant(i)); + + ir_instruction *assignment = new(ctx) ir_assignment(lhs, rhs, NULL); + instructions->push_tail(assignment); + + i++; + } + + return new(ctx) ir_dereference_variable(var); +} + + +/** + * Try to convert a record constructor to a constant expression + */ +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(); + if (constant == NULL) + return NULL; + node->replace_with(constant); + } + + return new(mem_ctx) ir_constant(constructor_type, parameters); +} + + +/** + * Determine if a list consists of a single scalar r-value + */ +bool +single_scalar_parameter(exec_list *parameters) +{ + const ir_rvalue *const p = (ir_rvalue *) parameters->head; + assert(((ir_rvalue *)p)->as_rvalue() != NULL); + + return (p->type->is_scalar() && p->next->is_tail_sentinel()); +} + + +/** + * Generate inline code for a vector constructor + * + * The generated constructor code will consist of a temporary variable + * declaration of the same type as the constructor. A sequence of assignments + * from constructor parameters to the temporary will follow. + * + * \return + * An \c ir_dereference_variable of the temprorary generated in the constructor + * body. + */ +ir_rvalue * +emit_inline_vector_constructor(const glsl_type *type, + exec_list *instructions, + exec_list *parameters, + void *ctx) +{ + assert(!parameters->is_empty()); + + ir_variable *var = new(ctx) ir_variable(type, "vec_ctor", ir_var_temporary); + instructions->push_tail(var); + + /* There are two kinds of vector constructors. + * + * - Construct a vector from a single scalar by replicating that scalar to + * all components of the vector. + * + * - Construct a vector from an arbirary combination of vectors and + * scalars. The components of the constructor parameters are assigned + * to the vector in order until the vector is full. + */ + const unsigned lhs_components = type->components(); + if (single_scalar_parameter(parameters)) { + ir_rvalue *first_param = (ir_rvalue *)parameters->head; + 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, 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 *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_lhs_component) > lhs_components) { + rhs_components = lhs_components - base_lhs_component; + } + + 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. + */ + 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_dereference *lhs = new(ctx) ir_dereference_variable(var); + + /* 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. + */ + base_component += rhs_components; + } + } + return new(ctx) ir_dereference_variable(var); +} + + +/** + * Generate assignment of a portion of a vector to a portion of a matrix column + * + * \param src_base First component of the source to be used in assignment + * \param column Column of destination to be assiged + * \param row_base First component of the destination column to be assigned + * \param count Number of components to be assigned + * + * \note + * \c src_base + \c count must be less than or equal to the number of components + * in the source vector. + */ +ir_instruction * +assign_to_matrix_column(ir_variable *var, unsigned column, unsigned row_base, + ir_rvalue *src, unsigned src_base, unsigned count, + void *mem_ctx) +{ + 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)); + assert(src->type->components() >= (src_base + count)); + + /* 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); +} + + +/** + * Generate inline code for a matrix constructor + * + * The generated constructor code will consist of a temporary variable + * declaration of the same type as the constructor. A sequence of assignments + * from constructor parameters to the temporary will follow. + * + * \return + * An \c ir_dereference_variable of the temprorary generated in the constructor + * body. + */ +ir_rvalue * +emit_inline_matrix_constructor(const glsl_type *type, + exec_list *instructions, + exec_list *parameters, + void *ctx) +{ + assert(!parameters->is_empty()); + + ir_variable *var = new(ctx) ir_variable(type, "mat_ctor", ir_var_temporary); + instructions->push_tail(var); + + /* There are three kinds of matrix constructors. + * + * - Construct a matrix from a single scalar by replicating that scalar to + * along the diagonal of the matrix and setting all other components to + * zero. + * + * - Construct a matrix from an arbirary combination of vectors and + * scalars. The components of the constructor parameters are assigned + * to the matrix in colum-major order until the matrix is full. + * + * - Construct a matrix from a single matrix. The source matrix is copied + * to the upper left portion of the constructed matrix, and the remaining + * elements take values from the identity matrix. + */ + ir_rvalue *const first_param = (ir_rvalue *) parameters->head; + if (single_scalar_parameter(parameters)) { + /* Assign the scalar to the X component of a vec4, and fill the remaining + * components with zero. + */ + ir_variable *rhs_var = + new(ctx) ir_variable(glsl_type::vec4_type, "mat_ctor_vec", + ir_var_temporary); + instructions->push_tail(rhs_var); + + ir_constant_data zero; + zero.f[0] = 0.0; + zero.f[1] = 0.0; + zero.f[2] = 0.0; + zero.f[3] = 0.0; + + ir_instruction *inst = + new(ctx) ir_assignment(new(ctx) ir_dereference_variable(rhs_var), + new(ctx) ir_constant(rhs_var->type, &zero), + NULL); + instructions->push_tail(inst); + + ir_dereference *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); + + 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 + * with a swizzle that puts the X component on the diagonal of the + * matrix. In some cases this may mean that the X component does not + * get assigned into the column at all (i.e., when the matrix has more + * columns than rows). + */ + static const unsigned rhs_swiz[4][4] = { + { 0, 1, 1, 1 }, + { 1, 0, 1, 1 }, + { 1, 1, 0, 1 }, + { 1, 1, 1, 0 } + }; + + 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); + + ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); + ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, rhs_swiz[i], + type->vector_elements); + + inst = new(ctx) ir_assignment(col_ref, rhs, NULL); + instructions->push_tail(inst); + } + + for (unsigned i = cols_to_init; i < type->matrix_columns; i++) { + ir_constant *const col_idx = new(ctx) ir_constant(i); + ir_rvalue *const col_ref = new(ctx) ir_dereference_array(var, col_idx); + + ir_rvalue *const rhs_ref = new(ctx) ir_dereference_variable(rhs_var); + ir_rvalue *const rhs = new(ctx) ir_swizzle(rhs_ref, 1, 1, 1, 1, + type->vector_elements); + + inst = new(ctx) ir_assignment(col_ref, rhs, NULL); + instructions->push_tail(inst); + } + } else if (first_param->type->is_matrix()) { + /* From page 50 (56 of the PDF) of the GLSL 1.50 spec: + * + * "If a matrix is constructed from a matrix, then each component + * (column i, row j) in the result that has a corresponding + * component (column i, row j) in the argument will be initialized + * from there. All other components will be initialized to the + * identity matrix. If a matrix argument is given to a matrix + * constructor, it is an error to have any other arguments." + */ + assert(first_param->next->is_tail_sentinel()); + ir_rvalue *const src_matrix = first_param; + + /* If the source matrix is smaller, pre-initialize the relavent parts of + * the destination matrix to the identity matrix. + */ + if ((src_matrix->type->matrix_columns < var->type->matrix_columns) + || (src_matrix->type->vector_elements < var->type->vector_elements)) { + + /* If the source matrix has fewer rows, every column of the destination + * must be initialized. Otherwise only the columns in the destination + * that do not exist in the source must be initialized. + */ + unsigned col = + (src_matrix->type->vector_elements < var->type->vector_elements) + ? 0 : src_matrix->type->matrix_columns; + + const glsl_type *const col_type = var->type->column_type(); + for (/* empty */; col < var->type->matrix_columns; col++) { + ir_constant_data ident; + + ident.f[0] = 0.0; + ident.f[1] = 0.0; + ident.f[2] = 0.0; + ident.f[3] = 0.0; + + ident.f[col] = 1.0; + + ir_rvalue *const rhs = new(ctx) ir_constant(col_type, &ident); + + ir_rvalue *const lhs = + new(ctx) ir_dereference_array(var, new(ctx) ir_constant(col)); + + ir_instruction *inst = new(ctx) ir_assignment(lhs, rhs, NULL); + instructions->push_tail(inst); + } + } + + /* Assign columns from the source matrix to the destination matrix. + * + * Since the parameter will be used in the RHS of multiple assignments, + * generate a temporary and copy the paramter there. + */ + ir_variable *const rhs_var = + new(ctx) ir_variable(first_param->type, "mat_ctor_mat", + ir_var_temporary); + instructions->push_tail(rhs_var); + + ir_dereference *const rhs_var_ref = + new(ctx) ir_dereference_variable(rhs_var); + ir_instruction *const inst = + 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; + + for (unsigned i = 0; i < last_col; i++) { + 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)); + + /* If one matrix has columns that are smaller than the columns of the + * other matrix, wrap the column access of the larger with a swizzle + * so that the LHS and RHS of the assignment have the same size (and + * therefore have the same type). + * + * It would be perfectly valid to unconditionally generate the + * swizzles, this this will typically result in a more compact IR tree. + */ + ir_rvalue *rhs; + if (lhs->type->vector_elements != rhs_col->type->vector_elements) { + rhs = new(ctx) ir_swizzle(rhs_col, swiz, last_row); + } else { + rhs = rhs_col; + } + + ir_instruction *inst = + new(ctx) ir_assignment(lhs, rhs, NULL, write_mask); + instructions->push_tail(inst); + } + } else { + const unsigned cols = type->matrix_columns; + const unsigned rows = type->vector_elements; + unsigned col_idx = 0; + unsigned row_idx = 0; + + foreach_list (node, parameters) { + ir_rvalue *const rhs = (ir_rvalue *) node; + const unsigned components_remaining_this_column = rows - row_idx; + unsigned rhs_components = rhs->type->components(); + unsigned rhs_base = 0; + + /* Since the parameter might be used in the RHS of two assignments, + * generate a temporary and copy the paramter there. + */ + ir_variable *rhs_var = + new(ctx) ir_variable(rhs->type, "mat_ctor_vec", ir_var_temporary); + instructions->push_tail(rhs_var); + + ir_dereference *rhs_var_ref = + new(ctx) ir_dereference_variable(rhs_var); + ir_instruction *inst = new(ctx) ir_assignment(rhs_var_ref, rhs, NULL); + instructions->push_tail(inst); + + /* Assign the current parameter to as many components of the matrix + * as it will fill. + * + * NOTE: A single vector parameter can span two matrix columns. A + * single vec4, for example, can completely fill a mat2. + */ + if (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); + + ir_instruction *inst = assign_to_matrix_column(var, col_idx, + row_idx, + rhs_var_ref, 0, + count, ctx); + instructions->push_tail(inst); + + rhs_base = count; + + col_idx++; + row_idx = 0; + } + + /* If there is data left in the parameter and components left to be + * set in the destination, emit another assignment. It is possible + * that the assignment could be of a vec4 to the last element of the + * matrix. In this case col_idx==cols, but there is still data + * left in the source parameter. Obviously, don't emit an assignment + * to data outside the destination matrix. + */ + if ((col_idx < cols) && (rhs_base < rhs_components)) { + const unsigned count = rhs_components - rhs_base; + + rhs_var_ref = new(ctx) ir_dereference_variable(rhs_var); + + ir_instruction *inst = assign_to_matrix_column(var, col_idx, + row_idx, + rhs_var_ref, + rhs_base, + count, ctx); + instructions->push_tail(inst); + + row_idx += count; + } + } + } + + return new(ctx) ir_dereference_variable(var); +} + + +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) +{ + void *ctx = state; + /* There are three sorts of function calls. + * + * 1. constructors - The first subexpression is an ast_type_specifier. + * 2. methods - Only the .length() method of array types. + * 3. functions - Calls to regular old functions. + * + * Method calls are actually detected when the ast_field_selection + * expression is handled. + */ + if (is_constructor()) { + const ast_type_specifier *type = (ast_type_specifier *) subexpressions[0]; + YYLTYPE loc = type->get_location(); + const char *name; + + 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_call::get_error_instruction(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); + } + + 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 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 + * 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. + */ + 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_call::get_error_instruction(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_call::get_error_instruction(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_call::get_error_instruction(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); + + /* Total number of components of the type being constructed. */ + const unsigned type_components = constructor_type->components(); + + /* Number of components from parameters that have actually been + * consumed. This is used to perform several kinds of error checking. + */ + unsigned components_used = 0; + + unsigned matrix_parameters = 0; + unsigned nonmatrix_parameters = 0; + exec_list actual_parameters; + + foreach_list (n, &this->expressions) { + ast_node *ast = exec_node_data(ast_node, n, link); + ir_rvalue *result = ast->hir(instructions, state)->as_rvalue(); + + /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: + * + * "It is an error to provide extra arguments beyond this + * last used argument." + */ + if (components_used >= type_components) { + _mesa_glsl_error(& loc, state, "too many parameters to `%s' " + "constructor", + constructor_type->name); + return ir_call::get_error_instruction(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); + } + + /* Count the number of matrix and nonmatrix parameters. This + * is used below to enforce some of the constructor rules. + */ + if (result->type->is_matrix()) + matrix_parameters++; + else + nonmatrix_parameters++; + + actual_parameters.push_tail(result); + components_used += result->type->components(); + } + + /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec: + * + * "It is an error to construct matrices from other matrices. This + * is reserved for future use." + */ + if (state->language_version == 110 && matrix_parameters > 0 + && constructor_type->is_matrix()) { + _mesa_glsl_error(& loc, state, "cannot construct `%s' from a " + "matrix in GLSL 1.10", + constructor_type->name); + return ir_call::get_error_instruction(ctx); + } + + /* From page 50 (page 56 of the PDF) of the GLSL 1.50 spec: + * + * "If a matrix argument is given to a matrix constructor, it is + * an error to have any other arguments." + */ + if ((matrix_parameters > 0) + && ((matrix_parameters + nonmatrix_parameters) > 1) + && constructor_type->is_matrix()) { + _mesa_glsl_error(& loc, state, "for matrix `%s' constructor, " + "matrix must be only parameter", + constructor_type->name); + return ir_call::get_error_instruction(ctx); + } + + /* From page 28 (page 34 of the PDF) of the GLSL 1.10 spec: + * + * "In these cases, there must be enough components provided in the + * arguments to provide an initializer for every component in the + * constructed value." + */ + if (components_used < type_components && 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); + } + + /* Later, we cast each parameter to the same base type as the + * constructor. Since there are no non-floating point matrices, we + * 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; + + if (!matrix->type->is_matrix()) + continue; + + /* Create a temporary containing the matrix. */ + ir_variable *var = new(ctx) ir_variable(matrix->type, "matrix_tmp", + ir_var_temporary); + instructions->push_tail(var); + instructions->push_tail(new(ctx) ir_assignment(new(ctx) + ir_dereference_variable(var), matrix, NULL)); + var->constant_value = matrix->constant_expression_value(); + + /* Replace the matrix with dereferences of its columns. */ + for (int i = 0; i < matrix->type->matrix_columns; i++) { + matrix->insert_before(new (ctx) ir_dereference_array(var, + new(ctx) ir_constant(i))); + } + matrix->remove(); + } + } + + 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; + + const glsl_type *desired_type = + glsl_type::get_instance(constructor_type->base_type, + ir->type->vector_elements, + ir->type->matrix_columns); + ir_rvalue *result = convert_component(ir, desired_type); + + /* 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; + + if (result != ir) { + ir->replace_with(result); + } + } + + /* If all of the parameters are trivially constant, create a + * constant representing the complete collection of parameters. + */ + if (all_parameters_are_constant) { + return new(ctx) ir_constant(constructor_type, &actual_parameters); + } else if (constructor_type->is_scalar()) { + return dereference_component((ir_rvalue *) actual_parameters.head, + 0); + } else if (constructor_type->is_vector()) { + return emit_inline_vector_constructor(constructor_type, + instructions, + &actual_parameters, + ctx); + } else { + assert(constructor_type->is_matrix()); + return emit_inline_matrix_constructor(constructor_type, + instructions, + &actual_parameters, + ctx); + } + } else { + const ast_expression *id = subexpressions[0]; + YYLTYPE loc = id->get_location(); + exec_list actual_parameters; + + process_parameters(instructions, &actual_parameters, &this->expressions, + state); + + return match_function_by_name(instructions, + id->primary_expression.identifier, & loc, + &actual_parameters, state); + } + + return ir_call::get_error_instruction(ctx); +} diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp index f9fd1d68a..484786c5f 100644 --- a/mesalib/src/glsl/ast_to_hir.cpp +++ b/mesalib/src/glsl/ast_to_hir.cpp @@ -1,3617 +1,3617 @@ -/* - * 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. - */ - -/** - * \file ast_to_hir.c - * Convert abstract syntax to to high-level intermediate reprensentation (HIR). - * - * During the conversion to HIR, the majority of the symantic checking is - * preformed on the program. This includes: - * - * * Symbol table management - * * Type checking - * * Function binding - * - * The majority of this work could be done during parsing, and the parser could - * probably generate HIR directly. However, this results in frequent changes - * to the parser code. Since we do not assume that every system this complier - * is built on will have Flex and Bison installed, we have to store the code - * generated by these tools in our version control system. In other parts of - * the system we've seen problems where a parser was changed but the generated - * code was not committed, merge conflicts where created because two developers - * had slightly different versions of Bison installed, etc. - * - * I have also noticed that running Bison generated parsers in GDB is very - * irritating. When you get a segfault on '$$ = $1->foo', you can't very - * well 'print $1' in GDB. - * - * As a result, my preference is to put as little C code as possible in the - * parser (and lexer) sources. - */ - -#include "main/core.h" /* for struct gl_extensions */ -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "ast.h" -#include "glsl_types.h" -#include "ir.h" - -void -_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) -{ - _mesa_glsl_initialize_variables(instructions, state); - _mesa_glsl_initialize_functions(state); - - state->symbols->language_version = state->language_version; - - state->current_function = NULL; - - state->toplevel_ir = instructions; - - /* Section 4.2 of the GLSL 1.20 specification states: - * "The built-in functions are scoped in a scope outside the global scope - * users declare global variables in. That is, a shader's global scope, - * available for user-defined functions and global variables, is nested - * inside the scope containing the built-in functions." - * - * Since built-in functions like ftransform() access built-in variables, - * it follows that those must be in the outer scope as well. - * - * We push scope here to create this nesting effect...but don't pop. - * This way, a shader's globals are still in the symbol table for use - * by the linker. - */ - state->symbols->push_scope(); - - foreach_list_typed (ast_node, ast, link, & state->translation_unit) - ast->hir(instructions, state); - - detect_recursion_unlinked(state, instructions); - - state->toplevel_ir = NULL; -} - - -/** - * If a conversion is available, convert one operand to a different type - * - * The \c from \c ir_rvalue is converted "in place". - * - * \param to Type that the operand it to be converted to - * \param from Operand that is being converted - * \param state GLSL compiler state - * - * \return - * If a conversion is possible (or unnecessary), \c true is returned. - * Otherwise \c false is returned. - */ -bool -apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - if (to->base_type == from->type->base_type) - return true; - - /* This conversion was added in GLSL 1.20. If the compilation mode is - * GLSL 1.10, the conversion is skipped. - */ - if (state->language_version < 120) - return false; - - /* From page 27 (page 33 of the PDF) of the GLSL 1.50 spec: - * - * "There are no implicit array or structure conversions. For - * example, an array of int cannot be implicitly converted to an - * array of float. There are no implicit conversions between - * signed and unsigned integers." - */ - /* FINISHME: The above comment is partially a lie. There is int/uint - * FINISHME: conversion for immediate constants. - */ - if (!to->is_float() || !from->type->is_numeric()) - return false; - - /* Convert to a floating point type with the same number of components - * as the original type - i.e. int to float, not int to vec4. - */ - to = glsl_type::get_instance(GLSL_TYPE_FLOAT, from->type->vector_elements, - from->type->matrix_columns); - - switch (from->type->base_type) { - case GLSL_TYPE_INT: - from = new(ctx) ir_expression(ir_unop_i2f, to, from, NULL); - break; - case GLSL_TYPE_UINT: - from = new(ctx) ir_expression(ir_unop_u2f, to, from, NULL); - break; - case GLSL_TYPE_BOOL: - from = new(ctx) ir_expression(ir_unop_b2f, to, from, NULL); - break; - default: - assert(0); - } - - return true; -} - - -static const struct glsl_type * -arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, - bool multiply, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - const glsl_type *type_a = value_a->type; - const glsl_type *type_b = value_b->type; - - /* From GLSL 1.50 spec, page 56: - * - * "The arithmetic binary operators add (+), subtract (-), - * multiply (*), and divide (/) operate on integer and - * floating-point scalars, vectors, and matrices." - */ - if (!type_a->is_numeric() || !type_b->is_numeric()) { - _mesa_glsl_error(loc, state, - "Operands to arithmetic operators must be numeric"); - return glsl_type::error_type; - } - - - /* "If one operand is floating-point based and the other is - * not, then the conversions from Section 4.1.10 "Implicit - * Conversions" are applied to the non-floating-point-based operand." - */ - if (!apply_implicit_conversion(type_a, value_b, state) - && !apply_implicit_conversion(type_b, value_a, state)) { - _mesa_glsl_error(loc, state, - "Could not implicitly convert operands to " - "arithmetic operator"); - return glsl_type::error_type; - } - type_a = value_a->type; - type_b = value_b->type; - - /* "If the operands are integer types, they must both be signed or - * both be unsigned." - * - * From this rule and the preceeding conversion it can be inferred that - * both types must be GLSL_TYPE_FLOAT, or GLSL_TYPE_UINT, or GLSL_TYPE_INT. - * The is_numeric check above already filtered out the case where either - * type is not one of these, so now the base types need only be tested for - * equality. - */ - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, - "base type mismatch for arithmetic operator"); - return glsl_type::error_type; - } - - /* "All arithmetic binary operators result in the same fundamental type - * (signed integer, unsigned integer, or floating-point) as the - * operands they operate on, after operand type conversion. After - * conversion, the following cases are valid - * - * * The two operands are scalars. In this case the operation is - * applied, resulting in a scalar." - */ - if (type_a->is_scalar() && type_b->is_scalar()) - return type_a; - - /* "* One operand is a scalar, and the other is a vector or matrix. - * In this case, the scalar operation is applied independently to each - * component of the vector or matrix, resulting in the same size - * vector or matrix." - */ - if (type_a->is_scalar()) { - if (!type_b->is_scalar()) - return type_b; - } else if (type_b->is_scalar()) { - return type_a; - } - - /* All of the combinations of , , - * , , and have been - * handled. - */ - assert(!type_a->is_scalar()); - assert(!type_b->is_scalar()); - - /* "* The two operands are vectors of the same size. In this case, the - * operation is done component-wise resulting in the same size - * vector." - */ - if (type_a->is_vector() && type_b->is_vector()) { - if (type_a == type_b) { - return type_a; - } else { - _mesa_glsl_error(loc, state, - "vector size mismatch for arithmetic operator"); - return glsl_type::error_type; - } - } - - /* All of the combinations of , , - * , , , and - * have been handled. At least one of the operands must - * be matrix. Further, since there are no integer matrix types, the base - * type of both operands must be float. - */ - assert(type_a->is_matrix() || type_b->is_matrix()); - assert(type_a->base_type == GLSL_TYPE_FLOAT); - assert(type_b->base_type == GLSL_TYPE_FLOAT); - - /* "* The operator is add (+), subtract (-), or divide (/), and the - * operands are matrices with the same number of rows and the same - * number of columns. In this case, the operation is done component- - * wise resulting in the same size matrix." - * * The operator is multiply (*), where both operands are matrices or - * one operand is a vector and the other a matrix. A right vector - * operand is treated as a column vector and a left vector operand as a - * row vector. In all these cases, it is required that the number of - * columns of the left operand is equal to the number of rows of the - * right operand. Then, the multiply (*) operation does a linear - * algebraic multiply, yielding an object that has the same number of - * rows as the left operand and the same number of columns as the right - * operand. Section 5.10 "Vector and Matrix Operations" explains in - * more detail how vectors and matrices are operated on." - */ - if (! multiply) { - if (type_a == type_b) - return type_a; - } else { - if (type_a->is_matrix() && type_b->is_matrix()) { - /* Matrix multiply. The columns of A must match the rows of B. Given - * the other previously tested constraints, this means the vector type - * of a row from A must be the same as the vector type of a column from - * B. - */ - if (type_a->row_type() == type_b->column_type()) { - /* The resulting matrix has the number of columns of matrix B and - * the number of rows of matrix A. We get the row count of A by - * looking at the size of a vector that makes up a column. The - * transpose (size of a row) is done for B. - */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_a->column_type()->vector_elements, - type_b->row_type()->vector_elements); - assert(type != glsl_type::error_type); - - return type; - } - } else if (type_a->is_matrix()) { - /* A is a matrix and B is a column vector. Columns of A must match - * rows of B. Given the other previously tested constraints, this - * means the vector type of a row from A must be the same as the - * vector the type of B. - */ - if (type_a->row_type() == type_b) { - /* The resulting vector has a number of elements equal to - * the number of rows of matrix A. */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_a->column_type()->vector_elements, - 1); - assert(type != glsl_type::error_type); - - return type; - } - } else { - assert(type_b->is_matrix()); - - /* A is a row vector and B is a matrix. Columns of A must match rows - * of B. Given the other previously tested constraints, this means - * the type of A must be the same as the vector type of a column from - * B. - */ - if (type_a == type_b->column_type()) { - /* The resulting vector has a number of elements equal to - * the number of columns of matrix B. */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_b->row_type()->vector_elements, - 1); - assert(type != glsl_type::error_type); - - return type; - } - } - - _mesa_glsl_error(loc, state, "size mismatch for matrix multiplication"); - return glsl_type::error_type; - } - - - /* "All other cases are illegal." - */ - _mesa_glsl_error(loc, state, "type mismatch"); - return glsl_type::error_type; -} - - -static const struct glsl_type * -unary_arithmetic_result_type(const struct glsl_type *type, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - /* From GLSL 1.50 spec, page 57: - * - * "The arithmetic unary operators negate (-), post- and pre-increment - * and decrement (-- and ++) operate on integer or floating-point - * values (including vectors and matrices). All unary operators work - * component-wise on their operands. These result with the same type - * they operated on." - */ - if (!type->is_numeric()) { - _mesa_glsl_error(loc, state, - "Operands to arithmetic operators must be numeric"); - return glsl_type::error_type; - } - - return type; -} - -/** - * \brief Return the result type of a bit-logic operation. - * - * If the given types to the bit-logic operator are invalid, return - * glsl_type::error_type. - * - * \param type_a Type of LHS of bit-logic op - * \param type_b Type of RHS of bit-logic op - */ -static const struct glsl_type * -bit_logic_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - ast_operators op, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); - return glsl_type::error_type; - } - - /* From page 50 (page 56 of PDF) of GLSL 1.30 spec: - * - * "The bitwise operators and (&), exclusive-or (^), and inclusive-or - * (|). The operands must be of type signed or unsigned integers or - * integer vectors." - */ - if (!type_a->is_integer()) { - _mesa_glsl_error(loc, state, "LHS of `%s' must be an integer", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - if (!type_b->is_integer()) { - _mesa_glsl_error(loc, state, "RHS of `%s' must be an integer", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "The fundamental types of the operands (signed or unsigned) must - * match," - */ - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, "operands of `%s' must have the same " - "base type", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "The operands cannot be vectors of differing size." */ - if (type_a->is_vector() && - type_b->is_vector() && - type_a->vector_elements != type_b->vector_elements) { - _mesa_glsl_error(loc, state, "operands of `%s' cannot be vectors of " - "different sizes", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "If one operand is a scalar and the other a vector, the scalar is - * applied component-wise to the vector, resulting in the same type as - * the vector. The fundamental types of the operands [...] will be the - * resulting fundamental type." - */ - if (type_a->is_scalar()) - return type_b; - else - return type_a; -} - -static const struct glsl_type * -modulus_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, - "operator '%%' is reserved in %s", - state->version_string); - return glsl_type::error_type; - } - - /* From GLSL 1.50 spec, page 56: - * "The operator modulus (%) operates on signed or unsigned integers or - * integer vectors. The operand types must both be signed or both be - * unsigned." - */ - if (!type_a->is_integer()) { - _mesa_glsl_error(loc, state, "LHS of operator %% must be an integer."); - return glsl_type::error_type; - } - if (!type_b->is_integer()) { - _mesa_glsl_error(loc, state, "RHS of operator %% must be an integer."); - return glsl_type::error_type; - } - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, - "operands of %% must have the same base type"); - return glsl_type::error_type; - } - - /* "The operands cannot be vectors of differing size. If one operand is - * a scalar and the other vector, then the scalar is applied component- - * wise to the vector, resulting in the same type as the vector. If both - * are vectors of the same size, the result is computed component-wise." - */ - if (type_a->is_vector()) { - if (!type_b->is_vector() - || (type_a->vector_elements == type_b->vector_elements)) - return type_a; - } else - return type_b; - - /* "The operator modulus (%) is not defined for any other data types - * (non-integer types)." - */ - _mesa_glsl_error(loc, state, "type mismatch"); - return glsl_type::error_type; -} - - -static const struct glsl_type * -relational_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - const glsl_type *type_a = value_a->type; - const glsl_type *type_b = value_b->type; - - /* From GLSL 1.50 spec, page 56: - * "The relational operators greater than (>), less than (<), greater - * than or equal (>=), and less than or equal (<=) operate only on - * scalar integer and scalar floating-point expressions." - */ - if (!type_a->is_numeric() - || !type_b->is_numeric() - || !type_a->is_scalar() - || !type_b->is_scalar()) { - _mesa_glsl_error(loc, state, - "Operands to relational operators must be scalar and " - "numeric"); - return glsl_type::error_type; - } - - /* "Either the operands' types must match, or the conversions from - * Section 4.1.10 "Implicit Conversions" will be applied to the integer - * operand, after which the types must match." - */ - if (!apply_implicit_conversion(type_a, value_b, state) - && !apply_implicit_conversion(type_b, value_a, state)) { - _mesa_glsl_error(loc, state, - "Could not implicitly convert operands to " - "relational operator"); - return glsl_type::error_type; - } - type_a = value_a->type; - type_b = value_b->type; - - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, "base type mismatch"); - return glsl_type::error_type; - } - - /* "The result is scalar Boolean." - */ - return glsl_type::bool_type; -} - -/** - * \brief Return the result type of a bit-shift operation. - * - * If the given types to the bit-shift operator are invalid, return - * glsl_type::error_type. - * - * \param type_a Type of LHS of bit-shift op - * \param type_b Type of RHS of bit-shift op - */ -static const struct glsl_type * -shift_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - ast_operators op, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); - return glsl_type::error_type; - } - - /* From page 50 (page 56 of the PDF) of the GLSL 1.30 spec: - * - * "The shift operators (<<) and (>>). For both operators, the operands - * must be signed or unsigned integers or integer vectors. One operand - * can be signed while the other is unsigned." - */ - if (!type_a->is_integer()) { - _mesa_glsl_error(loc, state, "LHS of operator %s must be an integer or " - "integer vector", ast_expression::operator_string(op)); - return glsl_type::error_type; - - } - if (!type_b->is_integer()) { - _mesa_glsl_error(loc, state, "RHS of operator %s must be an integer or " - "integer vector", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "If the first operand is a scalar, the second operand has to be - * a scalar as well." - */ - if (type_a->is_scalar() && !type_b->is_scalar()) { - _mesa_glsl_error(loc, state, "If the first operand of %s is scalar, the " - "second must be scalar as well", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* If both operands are vectors, check that they have same number of - * elements. - */ - if (type_a->is_vector() && - type_b->is_vector() && - type_a->vector_elements != type_b->vector_elements) { - _mesa_glsl_error(loc, state, "Vector operands to operator %s must " - "have same number of elements", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "In all cases, the resulting type will be the same type as the left - * operand." - */ - return type_a; -} - -/** - * Validates that a value can be assigned to a location with a specified type - * - * Validates that \c rhs can be assigned to some location. If the types are - * not an exact match but an automatic conversion is possible, \c rhs will be - * converted. - * - * \return - * \c NULL if \c rhs cannot be assigned to a location with type \c lhs_type. - * Otherwise the actual RHS to be assigned will be returned. This may be - * \c rhs, or it may be \c rhs after some type conversion. - * - * \note - * In addition to being used for assignments, this function is used to - * type-check return values. - */ -ir_rvalue * -validate_assignment(struct _mesa_glsl_parse_state *state, - const glsl_type *lhs_type, ir_rvalue *rhs, - bool is_initializer) -{ - /* If there is already some error in the RHS, just return it. Anything - * else will lead to an avalanche of error message back to the user. - */ - if (rhs->type->is_error()) - return rhs; - - /* If the types are identical, the assignment can trivially proceed. - */ - if (rhs->type == lhs_type) - return rhs; - - /* If the array element types are the same and the size of the LHS is zero, - * the assignment is okay for initializers embedded in variable - * declarations. - * - * Note: Whole-array assignments are not permitted in GLSL 1.10, but this - * is handled by ir_dereference::is_lvalue. - */ - if (is_initializer && lhs_type->is_array() && rhs->type->is_array() - && (lhs_type->element_type() == rhs->type->element_type()) - && (lhs_type->array_size() == 0)) { - return rhs; - } - - /* Check for implicit conversion in GLSL 1.20 */ - if (apply_implicit_conversion(lhs_type, rhs, state)) { - if (rhs->type == lhs_type) - return rhs; - } - - return NULL; -} - -static void -mark_whole_array_access(ir_rvalue *access) -{ - ir_dereference_variable *deref = access->as_dereference_variable(); - - if (deref && deref->var) { - deref->var->max_array_access = deref->type->length - 1; - } -} - -ir_rvalue * -do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state, - ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer, - YYLTYPE lhs_loc) -{ - void *ctx = state; - bool error_emitted = (lhs->type->is_error() || rhs->type->is_error()); - - if (!error_emitted) { - if (lhs->variable_referenced() != NULL - && lhs->variable_referenced()->read_only) { - _mesa_glsl_error(&lhs_loc, state, - "assignment to read-only variable '%s'", - lhs->variable_referenced()->name); - error_emitted = true; - - } else if (state->language_version <= 110 && lhs->type->is_array()) { - /* From page 32 (page 38 of the PDF) of the GLSL 1.10 spec: - * - * "Other binary or unary expressions, non-dereferenced - * arrays, function names, swizzles with repeated fields, - * and constants cannot be l-values." - */ - _mesa_glsl_error(&lhs_loc, state, "whole array assignment is not " - "allowed in GLSL 1.10 or GLSL ES 1.00."); - error_emitted = true; - } else if (!lhs->is_lvalue()) { - _mesa_glsl_error(& lhs_loc, state, "non-lvalue in assignment"); - error_emitted = true; - } - } - - ir_rvalue *new_rhs = - validate_assignment(state, lhs->type, rhs, is_initializer); - if (new_rhs == NULL) { - _mesa_glsl_error(& lhs_loc, state, "type mismatch"); - } else { - rhs = new_rhs; - - /* If the LHS array was not declared with a size, it takes it size from - * the RHS. If the LHS is an l-value and a whole array, it must be a - * dereference of a variable. Any other case would require that the LHS - * is either not an l-value or not a whole array. - */ - if (lhs->type->array_size() == 0) { - ir_dereference *const d = lhs->as_dereference(); - - assert(d != NULL); - - ir_variable *const var = d->variable_referenced(); - - assert(var != NULL); - - if (var->max_array_access >= unsigned(rhs->type->array_size())) { - /* FINISHME: This should actually log the location of the RHS. */ - _mesa_glsl_error(& lhs_loc, state, "array size must be > %u due to " - "previous access", - var->max_array_access); - } - - var->type = glsl_type::get_array_instance(lhs->type->element_type(), - rhs->type->array_size()); - d->type = var->type; - } - mark_whole_array_access(rhs); - mark_whole_array_access(lhs); - } - - /* Most callers of do_assignment (assign, add_assign, pre_inc/dec, - * but not post_inc) need the converted assigned value as an rvalue - * to handle things like: - * - * i = j += 1; - * - * So we always just store the computed value being assigned to a - * temporary and return a deref of that temporary. If the rvalue - * ends up not being used, the temp will get copy-propagated out. - */ - ir_variable *var = new(ctx) ir_variable(rhs->type, "assignment_tmp", - ir_var_temporary); - ir_dereference_variable *deref_var = new(ctx) ir_dereference_variable(var); - instructions->push_tail(var); - instructions->push_tail(new(ctx) ir_assignment(deref_var, - rhs, - NULL)); - deref_var = new(ctx) ir_dereference_variable(var); - - if (!error_emitted) - instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var, NULL)); - - return new(ctx) ir_dereference_variable(var); -} - -static ir_rvalue * -get_lvalue_copy(exec_list *instructions, ir_rvalue *lvalue) -{ - void *ctx = ralloc_parent(lvalue); - ir_variable *var; - - var = new(ctx) ir_variable(lvalue->type, "_post_incdec_tmp", - ir_var_temporary); - instructions->push_tail(var); - var->mode = ir_var_auto; - - instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var), - lvalue, NULL)); - - /* Once we've created this temporary, mark it read only so it's no - * longer considered an lvalue. - */ - var->read_only = true; - - return new(ctx) ir_dereference_variable(var); -} - - -ir_rvalue * -ast_node::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - (void) instructions; - (void) state; - - return NULL; -} - -static ir_rvalue * -do_comparison(void *mem_ctx, int operation, ir_rvalue *op0, ir_rvalue *op1) -{ - int join_op; - ir_rvalue *cmp = NULL; - - if (operation == ir_binop_all_equal) - join_op = ir_binop_logic_and; - else - join_op = ir_binop_logic_or; - - switch (op0->type->base_type) { - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_BOOL: - return new(mem_ctx) ir_expression(operation, op0, op1); - - case GLSL_TYPE_ARRAY: { - for (unsigned int i = 0; i < op0->type->length; i++) { - ir_rvalue *e0, *e1, *result; - - e0 = new(mem_ctx) ir_dereference_array(op0->clone(mem_ctx, NULL), - new(mem_ctx) ir_constant(i)); - e1 = new(mem_ctx) ir_dereference_array(op1->clone(mem_ctx, NULL), - new(mem_ctx) ir_constant(i)); - result = do_comparison(mem_ctx, operation, e0, e1); - - if (cmp) { - cmp = new(mem_ctx) ir_expression(join_op, cmp, result); - } else { - cmp = result; - } - } - - mark_whole_array_access(op0); - mark_whole_array_access(op1); - break; - } - - case GLSL_TYPE_STRUCT: { - for (unsigned int i = 0; i < op0->type->length; i++) { - ir_rvalue *e0, *e1, *result; - const char *field_name = op0->type->fields.structure[i].name; - - e0 = new(mem_ctx) ir_dereference_record(op0->clone(mem_ctx, NULL), - field_name); - e1 = new(mem_ctx) ir_dereference_record(op1->clone(mem_ctx, NULL), - field_name); - result = do_comparison(mem_ctx, operation, e0, e1); - - if (cmp) { - cmp = new(mem_ctx) ir_expression(join_op, cmp, result); - } else { - cmp = result; - } - } - break; - } - - case GLSL_TYPE_ERROR: - case GLSL_TYPE_VOID: - case GLSL_TYPE_SAMPLER: - /* I assume a comparison of a struct containing a sampler just - * ignores the sampler present in the type. - */ - break; - - default: - assert(!"Should not get here."); - break; - } - - if (cmp == NULL) - cmp = new(mem_ctx) ir_constant(true); - - return cmp; -} - -/* For logical operations, we want to ensure that the operands are - * scalar booleans. If it isn't, emit an error and return a constant - * boolean to avoid triggering cascading error messages. - */ -ir_rvalue * -get_scalar_boolean_operand(exec_list *instructions, - struct _mesa_glsl_parse_state *state, - ast_expression *parent_expr, - int operand, - const char *operand_name, - bool *error_emitted) -{ - ast_expression *expr = parent_expr->subexpressions[operand]; - void *ctx = state; - ir_rvalue *val = expr->hir(instructions, state); - - if (val->type->is_boolean() && val->type->is_scalar()) - return val; - - if (!*error_emitted) { - YYLTYPE loc = expr->get_location(); - _mesa_glsl_error(&loc, state, "%s of `%s' must be scalar boolean", - operand_name, - parent_expr->operator_string(parent_expr->oper)); - *error_emitted = true; - } - - return new(ctx) ir_constant(true); -} - -/** - * If name refers to a builtin array whose maximum allowed size is less than - * size, report an error and return true. Otherwise return false. - */ -static bool -check_builtin_array_max_size(const char *name, unsigned size, - YYLTYPE loc, struct _mesa_glsl_parse_state *state) -{ - if ((strcmp("gl_TexCoord", name) == 0) - && (size > state->Const.MaxTextureCoords)) { - /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: - * - * "The size [of gl_TexCoord] can be at most - * gl_MaxTextureCoords." - */ - _mesa_glsl_error(&loc, state, "`gl_TexCoord' array size cannot " - "be larger than gl_MaxTextureCoords (%u)\n", - state->Const.MaxTextureCoords); - return true; - } else if (strcmp("gl_ClipDistance", name) == 0 - && size > state->Const.MaxClipPlanes) { - /* From section 7.1 (Vertex Shader Special Variables) of the - * GLSL 1.30 spec: - * - * "The gl_ClipDistance array is predeclared as unsized and - * must be sized by the shader either redeclaring it with a - * size or indexing it only with integral constant - * expressions. ... The size can be at most - * gl_MaxClipDistances." - */ - _mesa_glsl_error(&loc, state, "`gl_ClipDistance' array size cannot " - "be larger than gl_MaxClipDistances (%u)\n", - state->Const.MaxClipPlanes); - return true; - } - return false; -} - -ir_rvalue * -ast_expression::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - static const int operations[AST_NUM_OPERATORS] = { - -1, /* ast_assign doesn't convert to ir_expression. */ - -1, /* ast_plus doesn't convert to ir_expression. */ - ir_unop_neg, - ir_binop_add, - ir_binop_sub, - ir_binop_mul, - ir_binop_div, - ir_binop_mod, - ir_binop_lshift, - ir_binop_rshift, - ir_binop_less, - ir_binop_greater, - ir_binop_lequal, - ir_binop_gequal, - ir_binop_all_equal, - ir_binop_any_nequal, - ir_binop_bit_and, - ir_binop_bit_xor, - ir_binop_bit_or, - ir_unop_bit_not, - ir_binop_logic_and, - ir_binop_logic_xor, - ir_binop_logic_or, - ir_unop_logic_not, - - /* Note: The following block of expression types actually convert - * to multiple IR instructions. - */ - ir_binop_mul, /* ast_mul_assign */ - ir_binop_div, /* ast_div_assign */ - ir_binop_mod, /* ast_mod_assign */ - ir_binop_add, /* ast_add_assign */ - ir_binop_sub, /* ast_sub_assign */ - ir_binop_lshift, /* ast_ls_assign */ - ir_binop_rshift, /* ast_rs_assign */ - ir_binop_bit_and, /* ast_and_assign */ - ir_binop_bit_xor, /* ast_xor_assign */ - ir_binop_bit_or, /* ast_or_assign */ - - -1, /* ast_conditional doesn't convert to ir_expression. */ - ir_binop_add, /* ast_pre_inc. */ - ir_binop_sub, /* ast_pre_dec. */ - ir_binop_add, /* ast_post_inc. */ - ir_binop_sub, /* ast_post_dec. */ - -1, /* ast_field_selection doesn't conv to ir_expression. */ - -1, /* ast_array_index doesn't convert to ir_expression. */ - -1, /* ast_function_call doesn't conv to ir_expression. */ - -1, /* ast_identifier doesn't convert to ir_expression. */ - -1, /* ast_int_constant doesn't convert to ir_expression. */ - -1, /* ast_uint_constant doesn't conv to ir_expression. */ - -1, /* ast_float_constant doesn't conv to ir_expression. */ - -1, /* ast_bool_constant doesn't conv to ir_expression. */ - -1, /* ast_sequence doesn't convert to ir_expression. */ - }; - ir_rvalue *result = NULL; - ir_rvalue *op[3]; - const struct glsl_type *type; /* a temporary variable for switch cases */ - bool error_emitted = false; - YYLTYPE loc; - - loc = this->get_location(); - - switch (this->oper) { - case ast_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - result = do_assignment(instructions, state, op[0], op[1], false, - this->subexpressions[0]->get_location()); - error_emitted = result->type->is_error(); - break; - } - - case ast_plus: - op[0] = this->subexpressions[0]->hir(instructions, state); - - type = unary_arithmetic_result_type(op[0]->type, state, & loc); - - error_emitted = type->is_error(); - - result = op[0]; - break; - - case ast_neg: - op[0] = this->subexpressions[0]->hir(instructions, state); - - type = unary_arithmetic_result_type(op[0]->type, state, & loc); - - error_emitted = type->is_error(); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], NULL); - break; - - case ast_add: - case ast_sub: - case ast_mul: - case ast_div: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = arithmetic_result_type(op[0], op[1], - (this->oper == ast_mul), - state, & loc); - error_emitted = type->is_error(); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - break; - - case ast_mod: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); - - assert(operations[this->oper] == ir_binop_mod); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = type->is_error(); - break; - - case ast_lshift: - case ast_rshift: - if (state->language_version < 130) { - _mesa_glsl_error(&loc, state, "operator %s requires GLSL 1.30", - operator_string(this->oper)); - error_emitted = true; - } - - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, - &loc); - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - - case ast_less: - case ast_greater: - case ast_lequal: - case ast_gequal: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = relational_result_type(op[0], op[1], state, & loc); - - /* The relational operators must either generate an error or result - * in a scalar boolean. See page 57 of the GLSL 1.50 spec. - */ - assert(type->is_error() - || ((type->base_type == GLSL_TYPE_BOOL) - && type->is_scalar())); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = type->is_error(); - break; - - case ast_nequal: - case ast_equal: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - /* From page 58 (page 64 of the PDF) of the GLSL 1.50 spec: - * - * "The equality operators equal (==), and not equal (!=) - * operate on all types. They result in a scalar Boolean. If - * the operand types do not match, then there must be a - * conversion from Section 4.1.10 "Implicit Conversions" - * applied to one operand that can make them match, in which - * case this conversion is done." - */ - if ((!apply_implicit_conversion(op[0]->type, op[1], state) - && !apply_implicit_conversion(op[1]->type, op[0], state)) - || (op[0]->type != op[1]->type)) { - _mesa_glsl_error(& loc, state, "operands of `%s' must have the same " - "type", (this->oper == ast_equal) ? "==" : "!="); - error_emitted = true; - } else if ((state->language_version <= 110) - && (op[0]->type->is_array() || op[1]->type->is_array())) { - _mesa_glsl_error(& loc, state, "array comparisons forbidden in " - "GLSL 1.10"); - error_emitted = true; - } - - if (error_emitted) { - result = new(ctx) ir_constant(false); - } else { - result = do_comparison(ctx, operations[this->oper], op[0], op[1]); - assert(result->type == glsl_type::bool_type); - } - break; - - case ast_bit_and: - case ast_bit_xor: - case ast_bit_or: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, - state, &loc); - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - - case ast_bit_not: - op[0] = this->subexpressions[0]->hir(instructions, state); - - if (state->language_version < 130) { - _mesa_glsl_error(&loc, state, "bit-wise operations require GLSL 1.30"); - error_emitted = true; - } - - if (!op[0]->type->is_integer()) { - _mesa_glsl_error(&loc, state, "operand of `~' must be an integer"); - error_emitted = true; - } - - type = op[0]->type; - result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL); - break; - - case ast_logic_and: { - exec_list rhs_instructions; - op[0] = get_scalar_boolean_operand(instructions, state, this, 0, - "LHS", &error_emitted); - op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1, - "RHS", &error_emitted); - - ir_constant *op0_const = op[0]->constant_expression_value(); - if (op0_const) { - if (op0_const->value.b[0]) { - instructions->append_list(&rhs_instructions); - result = op[1]; - } else { - result = op0_const; - } - type = glsl_type::bool_type; - } else { - ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, - "and_tmp", - ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - stmt->then_instructions.append_list(&rhs_instructions); - ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, op[1], NULL); - stmt->then_instructions.push_tail(then_assign); - - ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false), NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - type = tmp->type; - } - break; - } - - case ast_logic_or: { - exec_list rhs_instructions; - op[0] = get_scalar_boolean_operand(instructions, state, this, 0, - "LHS", &error_emitted); - op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1, - "RHS", &error_emitted); - - ir_constant *op0_const = op[0]->constant_expression_value(); - if (op0_const) { - if (op0_const->value.b[0]) { - result = op0_const; - } else { - result = op[1]; - } - type = glsl_type::bool_type; - } else { - ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, - "or_tmp", - ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true), NULL); - stmt->then_instructions.push_tail(then_assign); - - stmt->else_instructions.append_list(&rhs_instructions); - ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, op[1], NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - type = tmp->type; - } - break; - } - - case ast_logic_xor: - /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec: - * - * "The logical binary operators and (&&), or ( | | ), and - * exclusive or (^^). They operate only on two Boolean - * expressions and result in a Boolean expression." - */ - op[0] = get_scalar_boolean_operand(instructions, state, this, 0, "LHS", - &error_emitted); - op[1] = get_scalar_boolean_operand(instructions, state, this, 1, "RHS", - &error_emitted); - - result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, - op[0], op[1]); - break; - - case ast_logic_not: - op[0] = get_scalar_boolean_operand(instructions, state, this, 0, - "operand", &error_emitted); - - result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, - op[0], NULL); - break; - - case ast_mul_assign: - case ast_div_assign: - case ast_add_assign: - case ast_sub_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = arithmetic_result_type(op[0], op[1], - (this->oper == ast_mul_assign), - state, & loc); - - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = (op[0]->type->is_error()); - - /* GLSL 1.10 does not allow array assignment. However, we don't have to - * explicitly test for this because none of the binary expression - * operators allow array operands either. - */ - - break; - } - - case ast_mod_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); - - assert(operations[this->oper] == ir_binop_mod); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = type->is_error(); - break; - } - - case ast_ls_assign: - case ast_rs_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, - &loc); - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], - type, op[0], op[1]); - result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), - temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - } - - case ast_and_assign: - case ast_xor_assign: - case ast_or_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, - state, &loc); - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], - type, op[0], op[1]); - result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), - temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - } - - case ast_conditional: { - /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: - * - * "The ternary selection operator (?:). It operates on three - * expressions (exp1 ? exp2 : exp3). This operator evaluates the - * first expression, which must result in a scalar Boolean." - */ - op[0] = get_scalar_boolean_operand(instructions, state, this, 0, - "condition", &error_emitted); - - /* The :? operator is implemented by generating an anonymous temporary - * followed by an if-statement. The last instruction in each branch of - * the if-statement assigns a value to the anonymous temporary. This - * temporary is the r-value of the expression. - */ - exec_list then_instructions; - exec_list else_instructions; - - op[1] = this->subexpressions[1]->hir(&then_instructions, state); - op[2] = this->subexpressions[2]->hir(&else_instructions, state); - - /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: - * - * "The second and third expressions can be any type, as - * long their types match, or there is a conversion in - * Section 4.1.10 "Implicit Conversions" that can be applied - * to one of the expressions to make their types match. This - * resulting matching type is the type of the entire - * expression." - */ - if ((!apply_implicit_conversion(op[1]->type, op[2], state) - && !apply_implicit_conversion(op[2]->type, op[1], state)) - || (op[1]->type != op[2]->type)) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, "Second and third operands of ?: " - "operator must have matching types."); - error_emitted = true; - type = glsl_type::error_type; - } else { - type = op[1]->type; - } - - /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec: - * - * "The second and third expressions must be the same type, but can - * be of any type other than an array." - */ - if ((state->language_version <= 110) && type->is_array()) { - _mesa_glsl_error(& loc, state, "Second and third operands of ?: " - "operator must not be arrays."); - error_emitted = true; - } - - ir_constant *cond_val = op[0]->constant_expression_value(); - ir_constant *then_val = op[1]->constant_expression_value(); - ir_constant *else_val = op[2]->constant_expression_value(); - - if (then_instructions.is_empty() - && else_instructions.is_empty() - && (cond_val != NULL) && (then_val != NULL) && (else_val != NULL)) { - result = (cond_val->value.b[0]) ? then_val : else_val; - } else { - ir_variable *const tmp = - new(ctx) ir_variable(type, "conditional_tmp", ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - then_instructions.move_nodes_to(& stmt->then_instructions); - ir_dereference *const then_deref = - new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, op[1], NULL); - stmt->then_instructions.push_tail(then_assign); - - else_instructions.move_nodes_to(& stmt->else_instructions); - ir_dereference *const else_deref = - new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, op[2], NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - } - break; - } - - case ast_pre_inc: - case ast_pre_dec: { - op[0] = this->subexpressions[0]->hir(instructions, state); - if (op[0]->type->base_type == GLSL_TYPE_FLOAT) - op[1] = new(ctx) ir_constant(1.0f); - else - op[1] = new(ctx) ir_constant(1); - - type = arithmetic_result_type(op[0], op[1], false, state, & loc); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = op[0]->type->is_error(); - break; - } - - case ast_post_inc: - case ast_post_dec: { - op[0] = this->subexpressions[0]->hir(instructions, state); - if (op[0]->type->base_type == GLSL_TYPE_FLOAT) - op[1] = new(ctx) ir_constant(1.0f); - else - op[1] = new(ctx) ir_constant(1); - - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - - type = arithmetic_result_type(op[0], op[1], false, state, & loc); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - /* Get a temporary of a copy of the lvalue before it's modified. - * This may get thrown away later. - */ - result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL)); - - (void)do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - - error_emitted = op[0]->type->is_error(); - break; - } - - case ast_field_selection: - result = _mesa_ast_field_selection_to_hir(this, instructions, state); - break; - - case ast_array_index: { - YYLTYPE index_loc = subexpressions[1]->get_location(); - - op[0] = subexpressions[0]->hir(instructions, state); - op[1] = subexpressions[1]->hir(instructions, state); - - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - - ir_rvalue *const array = op[0]; - - result = new(ctx) ir_dereference_array(op[0], op[1]); - - /* Do not use op[0] after this point. Use array. - */ - op[0] = NULL; - - - if (error_emitted) - break; - - if (!array->type->is_array() - && !array->type->is_matrix() - && !array->type->is_vector()) { - _mesa_glsl_error(& index_loc, state, - "cannot dereference non-array / non-matrix / " - "non-vector"); - error_emitted = true; - } - - if (!op[1]->type->is_integer()) { - _mesa_glsl_error(& index_loc, state, - "array index must be integer type"); - error_emitted = true; - } else if (!op[1]->type->is_scalar()) { - _mesa_glsl_error(& index_loc, state, - "array index must be scalar"); - error_emitted = true; - } - - /* If the array index is a constant expression and the array has a - * declared size, ensure that the access is in-bounds. If the array - * index is not a constant expression, ensure that the array has a - * declared size. - */ - ir_constant *const const_index = op[1]->constant_expression_value(); - if (const_index != NULL) { - const int idx = const_index->value.i[0]; - const char *type_name; - unsigned bound = 0; - - if (array->type->is_matrix()) { - type_name = "matrix"; - } else if (array->type->is_vector()) { - type_name = "vector"; - } else { - type_name = "array"; - } - - /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec: - * - * "It is illegal to declare an array with a size, and then - * later (in the same shader) index the same array with an - * integral constant expression greater than or equal to the - * declared size. It is also illegal to index an array with a - * negative constant expression." - */ - if (array->type->is_matrix()) { - if (array->type->row_type()->vector_elements <= idx) { - bound = array->type->row_type()->vector_elements; - } - } else if (array->type->is_vector()) { - if (array->type->vector_elements <= idx) { - bound = array->type->vector_elements; - } - } else { - if ((array->type->array_size() > 0) - && (array->type->array_size() <= idx)) { - bound = array->type->array_size(); - } - } - - if (bound > 0) { - _mesa_glsl_error(& loc, state, "%s index must be < %u", - type_name, bound); - error_emitted = true; - } else if (idx < 0) { - _mesa_glsl_error(& loc, state, "%s index must be >= 0", - type_name); - error_emitted = true; - } - - if (array->type->is_array()) { - /* If the array is a variable dereference, it dereferences the - * whole array, by definition. Use this to get the variable. - * - * FINISHME: Should some methods for getting / setting / testing - * FINISHME: array access limits be added to ir_dereference? - */ - ir_variable *const v = array->whole_variable_referenced(); - if ((v != NULL) && (unsigned(idx) > v->max_array_access)) { - v->max_array_access = idx; - - /* Check whether this access will, as a side effect, implicitly - * cause the size of a built-in array to be too large. - */ - if (check_builtin_array_max_size(v->name, idx+1, loc, state)) - error_emitted = true; - } - } - } else if (array->type->array_size() == 0) { - _mesa_glsl_error(&loc, state, "unsized array index must be constant"); - } else { - if (array->type->is_array()) { - /* whole_variable_referenced can return NULL if the array is a - * member of a structure. In this case it is safe to not update - * the max_array_access field because it is never used for fields - * of structures. - */ - ir_variable *v = array->whole_variable_referenced(); - if (v != NULL) - v->max_array_access = array->type->array_size() - 1; - } - } - - /* From page 23 (29 of the PDF) of the GLSL 1.30 spec: - * - * "Samplers aggregated into arrays within a shader (using square - * brackets [ ]) can only be indexed with integral constant - * expressions [...]." - * - * This restriction was added in GLSL 1.30. Shaders using earlier version - * of the language should not be rejected by the compiler front-end for - * using this construct. This allows useful things such as using a loop - * counter as the index to an array of samplers. If the loop in unrolled, - * the code should compile correctly. Instead, emit a warning. - */ - if (array->type->is_array() && - array->type->element_type()->is_sampler() && - const_index == NULL) { - - if (state->language_version == 100) { - _mesa_glsl_warning(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is optional in GLSL ES 1.00"); - } else if (state->language_version < 130) { - _mesa_glsl_warning(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is forbidden in GLSL 1.30 and " - "later"); - } else { - _mesa_glsl_error(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is forbidden in GLSL 1.30 and " - "later"); - error_emitted = true; - } - } - - if (error_emitted) - result->type = glsl_type::error_type; - - break; - } - - case ast_function_call: - /* Should *NEVER* get here. ast_function_call should always be handled - * by ast_function_expression::hir. - */ - assert(0); - break; - - case ast_identifier: { - /* ast_identifier can appear several places in a full abstract syntax - * tree. This particular use must be at location specified in the grammar - * as 'variable_identifier'. - */ - ir_variable *var = - state->symbols->get_variable(this->primary_expression.identifier); - - result = new(ctx) ir_dereference_variable(var); - - if (var != NULL) { - var->used = true; - } else { - _mesa_glsl_error(& loc, state, "`%s' undeclared", - this->primary_expression.identifier); - - error_emitted = true; - } - break; - } - - case ast_int_constant: - result = new(ctx) ir_constant(this->primary_expression.int_constant); - break; - - case ast_uint_constant: - result = new(ctx) ir_constant(this->primary_expression.uint_constant); - break; - - case ast_float_constant: - result = new(ctx) ir_constant(this->primary_expression.float_constant); - break; - - case ast_bool_constant: - result = new(ctx) ir_constant(bool(this->primary_expression.bool_constant)); - break; - - case ast_sequence: { - /* It should not be possible to generate a sequence in the AST without - * any expressions in it. - */ - assert(!this->expressions.is_empty()); - - /* The r-value of a sequence is the last expression in the sequence. If - * the other expressions in the sequence do not have side-effects (and - * therefore add instructions to the instruction list), they get dropped - * on the floor. - */ - exec_node *previous_tail_pred = NULL; - YYLTYPE previous_operand_loc = loc; - - foreach_list_typed (ast_node, ast, link, &this->expressions) { - /* If one of the operands of comma operator does not generate any - * code, we want to emit a warning. At each pass through the loop - * previous_tail_pred will point to the last instruction in the - * stream *before* processing the previous operand. Naturally, - * instructions->tail_pred will point to the last instruction in the - * stream *after* processing the previous operand. If the two - * pointers match, then the previous operand had no effect. - * - * The warning behavior here differs slightly from GCC. GCC will - * only emit a warning if none of the left-hand operands have an - * effect. However, it will emit a warning for each. I believe that - * there are some cases in C (especially with GCC extensions) where - * it is useful to have an intermediate step in a sequence have no - * effect, but I don't think these cases exist in GLSL. Either way, - * it would be a giant hassle to replicate that behavior. - */ - if (previous_tail_pred == instructions->tail_pred) { - _mesa_glsl_warning(&previous_operand_loc, state, - "left-hand operand of comma expression has " - "no effect"); - } - - /* tail_pred is directly accessed instead of using the get_tail() - * method for performance reasons. get_tail() has extra code to - * return NULL when the list is empty. We don't care about that - * here, so using tail_pred directly is fine. - */ - previous_tail_pred = instructions->tail_pred; - previous_operand_loc = ast->get_location(); - - result = ast->hir(instructions, state); - } - - /* Any errors should have already been emitted in the loop above. - */ - error_emitted = true; - break; - } - } - type = NULL; /* use result->type, not type. */ - assert(result != NULL); - - if (result->type->is_error() && !error_emitted) - _mesa_glsl_error(& loc, state, "type mismatch"); - - return result; -} - - -ir_rvalue * -ast_expression_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - /* It is possible to have expression statements that don't have an - * expression. This is the solitary semicolon: - * - * for (i = 0; i < 5; i++) - * ; - * - * In this case the expression will be NULL. Test for NULL and don't do - * anything in that case. - */ - if (expression != NULL) - expression->hir(instructions, state); - - /* Statements do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_compound_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - if (new_scope) - state->symbols->push_scope(); - - foreach_list_typed (ast_node, ast, link, &this->statements) - ast->hir(instructions, state); - - if (new_scope) - state->symbols->pop_scope(); - - /* Compound statements do not have r-values. - */ - return NULL; -} - - -static const glsl_type * -process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size, - struct _mesa_glsl_parse_state *state) -{ - unsigned length = 0; - - /* FINISHME: Reject delcarations of multidimensional arrays. */ - - if (array_size != NULL) { - exec_list dummy_instructions; - ir_rvalue *const ir = array_size->hir(& dummy_instructions, state); - YYLTYPE loc = array_size->get_location(); - - if (ir != NULL) { - if (!ir->type->is_integer()) { - _mesa_glsl_error(& loc, state, "array size must be integer type"); - } else if (!ir->type->is_scalar()) { - _mesa_glsl_error(& loc, state, "array size must be scalar type"); - } else { - ir_constant *const size = ir->constant_expression_value(); - - if (size == NULL) { - _mesa_glsl_error(& loc, state, "array size must be a " - "constant valued expression"); - } else if (size->value.i[0] <= 0) { - _mesa_glsl_error(& loc, state, "array size must be > 0"); - } else { - assert(size->type == ir->type); - length = size->value.u[0]; - - /* If the array size is const (and we've verified that - * it is) then no instructions should have been emitted - * when we converted it to HIR. If they were emitted, - * then either the array size isn't const after all, or - * we are emitting unnecessary instructions. - */ - assert(dummy_instructions.is_empty()); - } - } - } - } else if (state->es_shader) { - /* Section 10.17 of the GLSL ES 1.00 specification states that unsized - * array declarations have been removed from the language. - */ - _mesa_glsl_error(loc, state, "unsized array declarations are not " - "allowed in GLSL ES 1.00."); - } - - return glsl_type::get_array_instance(base, length); -} - - -const glsl_type * -ast_type_specifier::glsl_type(const char **name, - struct _mesa_glsl_parse_state *state) const -{ - const struct glsl_type *type; - - type = state->symbols->get_type(this->type_name); - *name = this->type_name; - - if (this->is_array) { - YYLTYPE loc = this->get_location(); - type = process_array_type(&loc, type, this->array_size, state); - } - - return type; -} - - -static void -apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual, - ir_variable *var, - struct _mesa_glsl_parse_state *state, - YYLTYPE *loc) -{ - if (qual->flags.q.invariant) { - if (var->used) { - _mesa_glsl_error(loc, state, - "variable `%s' may not be redeclared " - "`invariant' after being used", - var->name); - } else { - var->invariant = 1; - } - } - - if (qual->flags.q.constant || qual->flags.q.attribute - || qual->flags.q.uniform - || (qual->flags.q.varying && (state->target == fragment_shader))) - var->read_only = 1; - - if (qual->flags.q.centroid) - var->centroid = 1; - - if (qual->flags.q.attribute && state->target != vertex_shader) { - var->type = glsl_type::error_type; - _mesa_glsl_error(loc, state, - "`attribute' variables may not be declared in the " - "%s shader", - _mesa_glsl_shader_target_name(state->target)); - } - - /* From page 25 (page 31 of the PDF) of the GLSL 1.10 spec: - * - * "The varying qualifier can be used only with the data types - * float, vec2, vec3, vec4, mat2, mat3, and mat4, or arrays of - * these." - */ - if (qual->flags.q.varying) { - const glsl_type *non_array_type; - - if (var->type && var->type->is_array()) - non_array_type = var->type->fields.array; - else - non_array_type = var->type; - - if (non_array_type && non_array_type->base_type != GLSL_TYPE_FLOAT) { - var->type = glsl_type::error_type; - _mesa_glsl_error(loc, state, - "varying variables must be of base type float"); - } - } - - /* If there is no qualifier that changes the mode of the variable, leave - * the setting alone. - */ - if (qual->flags.q.in && qual->flags.q.out) - var->mode = ir_var_inout; - else if (qual->flags.q.attribute || qual->flags.q.in - || (qual->flags.q.varying && (state->target == fragment_shader))) - var->mode = ir_var_in; - else if (qual->flags.q.out - || (qual->flags.q.varying && (state->target == vertex_shader))) - var->mode = ir_var_out; - else if (qual->flags.q.uniform) - var->mode = ir_var_uniform; - - if (state->all_invariant && (state->current_function == NULL)) { - switch (state->target) { - case vertex_shader: - if (var->mode == ir_var_out) - var->invariant = true; - break; - case geometry_shader: - if ((var->mode == ir_var_in) || (var->mode == ir_var_out)) - var->invariant = true; - break; - case fragment_shader: - if (var->mode == ir_var_in) - var->invariant = true; - break; - } - } - - if (qual->flags.q.flat) - var->interpolation = ir_var_flat; - else if (qual->flags.q.noperspective) - var->interpolation = ir_var_noperspective; - else - var->interpolation = ir_var_smooth; - - var->pixel_center_integer = qual->flags.q.pixel_center_integer; - var->origin_upper_left = qual->flags.q.origin_upper_left; - if ((qual->flags.q.origin_upper_left || qual->flags.q.pixel_center_integer) - && (strcmp(var->name, "gl_FragCoord") != 0)) { - const char *const qual_string = (qual->flags.q.origin_upper_left) - ? "origin_upper_left" : "pixel_center_integer"; - - _mesa_glsl_error(loc, state, - "layout qualifier `%s' can only be applied to " - "fragment shader input `gl_FragCoord'", - qual_string); - } - - if (qual->flags.q.explicit_location) { - const bool global_scope = (state->current_function == NULL); - bool fail = false; - const char *string = ""; - - /* In the vertex shader only shader inputs can be given explicit - * locations. - * - * In the fragment shader only shader outputs can be given explicit - * locations. - */ - switch (state->target) { - case vertex_shader: - if (!global_scope || (var->mode != ir_var_in)) { - fail = true; - string = "input"; - } - break; - - case geometry_shader: - _mesa_glsl_error(loc, state, - "geometry shader variables cannot be given " - "explicit locations\n"); - break; - - case fragment_shader: - if (!global_scope || (var->mode != ir_var_out)) { - fail = true; - string = "output"; - } - break; - }; - - if (fail) { - _mesa_glsl_error(loc, state, - "only %s shader %s variables can be given an " - "explicit location\n", - _mesa_glsl_shader_target_name(state->target), - string); - } else { - var->explicit_location = true; - - /* This bit of silliness is needed because invalid explicit locations - * are supposed to be flagged during linking. Small negative values - * biased by VERT_ATTRIB_GENERIC0 or FRAG_RESULT_DATA0 could alias - * built-in values (e.g., -16+VERT_ATTRIB_GENERIC0 = VERT_ATTRIB_POS). - * The linker needs to be able to differentiate these cases. This - * ensures that negative values stay negative. - */ - if (qual->location >= 0) { - var->location = (state->target == vertex_shader) - ? (qual->location + VERT_ATTRIB_GENERIC0) - : (qual->location + FRAG_RESULT_DATA0); - } else { - var->location = qual->location; - } - } - } - - /* Does the declaration use the 'layout' keyword? - */ - const bool uses_layout = qual->flags.q.pixel_center_integer - || qual->flags.q.origin_upper_left - || qual->flags.q.explicit_location; - - /* Does the declaration use the deprecated 'attribute' or 'varying' - * keywords? - */ - const bool uses_deprecated_qualifier = qual->flags.q.attribute - || qual->flags.q.varying; - - /* Is the 'layout' keyword used with parameters that allow relaxed checking. - * Many implementations of GL_ARB_fragment_coord_conventions_enable and some - * implementations (only Mesa?) GL_ARB_explicit_attrib_location_enable - * allowed the layout qualifier to be used with 'varying' and 'attribute'. - * These extensions and all following extensions that add the 'layout' - * keyword have been modified to require the use of 'in' or 'out'. - * - * The following extension do not allow the deprecated keywords: - * - * GL_AMD_conservative_depth - * GL_ARB_gpu_shader5 - * GL_ARB_separate_shader_objects - * GL_ARB_tesselation_shader - * GL_ARB_transform_feedback3 - * GL_ARB_uniform_buffer_object - * - * It is unknown whether GL_EXT_shader_image_load_store or GL_NV_gpu_shader5 - * allow layout with the deprecated keywords. - */ - const bool relaxed_layout_qualifier_checking = - state->ARB_fragment_coord_conventions_enable; - - if (uses_layout && uses_deprecated_qualifier) { - if (relaxed_layout_qualifier_checking) { - _mesa_glsl_warning(loc, state, - "`layout' qualifier may not be used with " - "`attribute' or `varying'"); - } else { - _mesa_glsl_error(loc, state, - "`layout' qualifier may not be used with " - "`attribute' or `varying'"); - } - } - - /* Layout qualifiers for gl_FragDepth, which are enabled by extension - * AMD_conservative_depth. - */ - int depth_layout_count = qual->flags.q.depth_any - + qual->flags.q.depth_greater - + qual->flags.q.depth_less - + qual->flags.q.depth_unchanged; - if (depth_layout_count > 0 - && !state->AMD_conservative_depth_enable) { - _mesa_glsl_error(loc, state, - "extension GL_AMD_conservative_depth must be enabled " - "to use depth layout qualifiers"); - } else if (depth_layout_count > 0 - && strcmp(var->name, "gl_FragDepth") != 0) { - _mesa_glsl_error(loc, state, - "depth layout qualifiers can be applied only to " - "gl_FragDepth"); - } else if (depth_layout_count > 1 - && strcmp(var->name, "gl_FragDepth") == 0) { - _mesa_glsl_error(loc, state, - "at most one depth layout qualifier can be applied to " - "gl_FragDepth"); - } - if (qual->flags.q.depth_any) - var->depth_layout = ir_depth_layout_any; - else if (qual->flags.q.depth_greater) - var->depth_layout = ir_depth_layout_greater; - else if (qual->flags.q.depth_less) - var->depth_layout = ir_depth_layout_less; - else if (qual->flags.q.depth_unchanged) - var->depth_layout = ir_depth_layout_unchanged; - else - var->depth_layout = ir_depth_layout_none; - - /* From page 46 (page 52 of the PDF) of the GLSL ES specification: - * - * "Array variables are l-values and may be passed to parameters - * declared as out or inout. However, they may not be used as - * the target of an assignment." - * - * From page 32 (page 38 of the PDF) of the GLSL 1.10 spec: - * - * "Other binary or unary expressions, non-dereferenced arrays, - * function names, swizzles with repeated fields, and constants - * cannot be l-values." - * - * So we only mark 1.10 as non-lvalues, and check for array - * assignment in 100 specifically in do_assignment. - */ - if (var->type->is_array() && state->language_version != 110) { - var->array_lvalue = true; - } -} - -/** - * Get the variable that is being redeclared by this declaration - * - * Semantic checks to verify the validity of the redeclaration are also - * performed. If semantic checks fail, compilation error will be emitted via - * \c _mesa_glsl_error, but a non-\c NULL pointer will still be returned. - * - * \returns - * A pointer to an existing variable in the current scope if the declaration - * is a redeclaration, \c NULL otherwise. - */ -ir_variable * -get_variable_being_redeclared(ir_variable *var, ast_declaration *decl, - struct _mesa_glsl_parse_state *state) -{ - /* Check if this declaration is actually a re-declaration, either to - * resize an array or add qualifiers to an existing variable. - * - * This is allowed for variables in the current scope, or when at - * global scope (for built-ins in the implicit outer scope). - */ - ir_variable *earlier = state->symbols->get_variable(decl->identifier); - if (earlier == NULL || - (state->current_function != NULL && - !state->symbols->name_declared_this_scope(decl->identifier))) { - return NULL; - } - - - YYLTYPE loc = decl->get_location(); - - /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec, - * - * "It is legal to declare an array without a size and then - * later re-declare the same name as an array of the same - * type and specify a size." - */ - if ((earlier->type->array_size() == 0) - && var->type->is_array() - && (var->type->element_type() == earlier->type->element_type())) { - /* FINISHME: This doesn't match the qualifiers on the two - * FINISHME: declarations. It's not 100% clear whether this is - * FINISHME: required or not. - */ - - const unsigned size = unsigned(var->type->array_size()); - check_builtin_array_max_size(var->name, size, loc, state); - if ((size > 0) && (size <= earlier->max_array_access)) { - _mesa_glsl_error(& loc, state, "array size must be > %u due to " - "previous access", - earlier->max_array_access); - } - - earlier->type = var->type; - delete var; - var = NULL; - } else if (state->ARB_fragment_coord_conventions_enable - && strcmp(var->name, "gl_FragCoord") == 0 - && earlier->type == var->type - && earlier->mode == var->mode) { - /* Allow redeclaration of gl_FragCoord for ARB_fcc layout - * qualifiers. - */ - earlier->origin_upper_left = var->origin_upper_left; - earlier->pixel_center_integer = var->pixel_center_integer; - - /* According to section 4.3.7 of the GLSL 1.30 spec, - * the following built-in varaibles can be redeclared with an - * interpolation qualifier: - * * gl_FrontColor - * * gl_BackColor - * * gl_FrontSecondaryColor - * * gl_BackSecondaryColor - * * gl_Color - * * gl_SecondaryColor - */ - } else if (state->language_version >= 130 - && (strcmp(var->name, "gl_FrontColor") == 0 - || strcmp(var->name, "gl_BackColor") == 0 - || strcmp(var->name, "gl_FrontSecondaryColor") == 0 - || strcmp(var->name, "gl_BackSecondaryColor") == 0 - || strcmp(var->name, "gl_Color") == 0 - || strcmp(var->name, "gl_SecondaryColor") == 0) - && earlier->type == var->type - && earlier->mode == var->mode) { - earlier->interpolation = var->interpolation; - - /* Layout qualifiers for gl_FragDepth. */ - } else if (state->AMD_conservative_depth_enable - && strcmp(var->name, "gl_FragDepth") == 0 - && earlier->type == var->type - && earlier->mode == var->mode) { - - /** From the AMD_conservative_depth spec: - * Within any shader, the first redeclarations of gl_FragDepth - * must appear before any use of gl_FragDepth. - */ - if (earlier->used) { - _mesa_glsl_error(&loc, state, - "the first redeclaration of gl_FragDepth " - "must appear before any use of gl_FragDepth"); - } - - /* Prevent inconsistent redeclaration of depth layout qualifier. */ - if (earlier->depth_layout != ir_depth_layout_none - && earlier->depth_layout != var->depth_layout) { - _mesa_glsl_error(&loc, state, - "gl_FragDepth: depth layout is declared here " - "as '%s, but it was previously declared as " - "'%s'", - depth_layout_string(var->depth_layout), - depth_layout_string(earlier->depth_layout)); - } - - earlier->depth_layout = var->depth_layout; - - } else { - _mesa_glsl_error(&loc, state, "`%s' redeclared", decl->identifier); - } - - return earlier; -} - -/** - * Generate the IR for an initializer in a variable declaration - */ -ir_rvalue * -process_initializer(ir_variable *var, ast_declaration *decl, - ast_fully_specified_type *type, - exec_list *initializer_instructions, - struct _mesa_glsl_parse_state *state) -{ - ir_rvalue *result = NULL; - - YYLTYPE initializer_loc = decl->initializer->get_location(); - - /* From page 24 (page 30 of the PDF) of the GLSL 1.10 spec: - * - * "All uniform variables are read-only and are initialized either - * directly by an application via API commands, or indirectly by - * OpenGL." - */ - if ((state->language_version <= 110) - && (var->mode == ir_var_uniform)) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize uniforms in GLSL 1.10"); - } - - if (var->type->is_sampler()) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize samplers"); - } - - if ((var->mode == ir_var_in) && (state->current_function == NULL)) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize %s shader input / %s", - _mesa_glsl_shader_target_name(state->target), - (state->target == vertex_shader) - ? "attribute" : "varying"); - } - - ir_dereference *const lhs = new(state) ir_dereference_variable(var); - ir_rvalue *rhs = decl->initializer->hir(initializer_instructions, - state); - - /* Calculate the constant value if this is a const or uniform - * declaration. - */ - if (type->qualifier.flags.q.constant - || type->qualifier.flags.q.uniform) { - ir_rvalue *new_rhs = validate_assignment(state, var->type, rhs, true); - if (new_rhs != NULL) { - rhs = new_rhs; - - ir_constant *constant_value = rhs->constant_expression_value(); - if (!constant_value) { - _mesa_glsl_error(& initializer_loc, state, - "initializer of %s variable `%s' must be a " - "constant expression", - (type->qualifier.flags.q.constant) - ? "const" : "uniform", - decl->identifier); - if (var->type->is_numeric()) { - /* Reduce cascading errors. */ - var->constant_value = ir_constant::zero(state, var->type); - } - } else { - rhs = constant_value; - var->constant_value = constant_value; - } - } else { - _mesa_glsl_error(&initializer_loc, state, - "initializer of type %s cannot be assigned to " - "variable of type %s", - rhs->type->name, var->type->name); - if (var->type->is_numeric()) { - /* Reduce cascading errors. */ - var->constant_value = ir_constant::zero(state, var->type); - } - } - } - - if (rhs && !rhs->type->is_error()) { - bool temp = var->read_only; - if (type->qualifier.flags.q.constant) - var->read_only = false; - - /* Never emit code to initialize a uniform. - */ - const glsl_type *initializer_type; - if (!type->qualifier.flags.q.uniform) { - result = do_assignment(initializer_instructions, state, - lhs, rhs, true, - type->get_location()); - initializer_type = result->type; - } else - initializer_type = rhs->type; - - /* If the declared variable is an unsized array, it must inherrit - * its full type from the initializer. A declaration such as - * - * uniform float a[] = float[](1.0, 2.0, 3.0, 3.0); - * - * becomes - * - * uniform float a[4] = float[](1.0, 2.0, 3.0, 3.0); - * - * The assignment generated in the if-statement (below) will also - * automatically handle this case for non-uniforms. - * - * If the declared variable is not an array, the types must - * already match exactly. As a result, the type assignment - * here can be done unconditionally. For non-uniforms the call - * to do_assignment can change the type of the initializer (via - * the implicit conversion rules). For uniforms the initializer - * must be a constant expression, and the type of that expression - * was validated above. - */ - var->type = initializer_type; - - var->read_only = temp; - } - - return result; -} - -ir_rvalue * -ast_declarator_list::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - const struct glsl_type *decl_type; - const char *type_name = NULL; - ir_rvalue *result = NULL; - YYLTYPE loc = this->get_location(); - - /* From page 46 (page 52 of the PDF) of the GLSL 1.50 spec: - * - * "To ensure that a particular output variable is invariant, it is - * necessary to use the invariant qualifier. It can either be used to - * qualify a previously declared variable as being invariant - * - * invariant gl_Position; // make existing gl_Position be invariant" - * - * In these cases the parser will set the 'invariant' flag in the declarator - * list, and the type will be NULL. - */ - if (this->invariant) { - assert(this->type == NULL); - - if (state->current_function != NULL) { - _mesa_glsl_error(& loc, state, - "All uses of `invariant' keyword must be at global " - "scope\n"); - } - - foreach_list_typed (ast_declaration, decl, link, &this->declarations) { - assert(!decl->is_array); - assert(decl->array_size == NULL); - assert(decl->initializer == NULL); - - ir_variable *const earlier = - state->symbols->get_variable(decl->identifier); - if (earlier == NULL) { - _mesa_glsl_error(& loc, state, - "Undeclared variable `%s' cannot be marked " - "invariant\n", decl->identifier); - } else if ((state->target == vertex_shader) - && (earlier->mode != ir_var_out)) { - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, vertex shader " - "outputs only\n", decl->identifier); - } else if ((state->target == fragment_shader) - && (earlier->mode != ir_var_in)) { - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, fragment shader " - "inputs only\n", decl->identifier); - } else if (earlier->used) { - _mesa_glsl_error(& loc, state, - "variable `%s' may not be redeclared " - "`invariant' after being used", - earlier->name); - } else { - earlier->invariant = true; - } - } - - /* Invariant redeclarations do not have r-values. - */ - return NULL; - } - - assert(this->type != NULL); - assert(!this->invariant); - - /* The type specifier may contain a structure definition. Process that - * before any of the variable declarations. - */ - (void) this->type->specifier->hir(instructions, state); - - decl_type = this->type->specifier->glsl_type(& type_name, state); - if (this->declarations.is_empty()) { - if (decl_type != NULL) { - /* Warn if this empty declaration is not for declaring a structure. - */ - if (this->type->specifier->structure == NULL) { - _mesa_glsl_warning(&loc, state, "empty declaration"); - } - } else { - _mesa_glsl_error(& loc, state, "incomplete declaration"); - } - } - - foreach_list_typed (ast_declaration, decl, link, &this->declarations) { - const struct glsl_type *var_type; - ir_variable *var; - - /* FINISHME: Emit a warning if a variable declaration shadows a - * FINISHME: declaration at a higher scope. - */ - - if ((decl_type == NULL) || decl_type->is_void()) { - if (type_name != NULL) { - _mesa_glsl_error(& loc, state, - "invalid type `%s' in declaration of `%s'", - type_name, decl->identifier); - } else { - _mesa_glsl_error(& loc, state, - "invalid type in declaration of `%s'", - decl->identifier); - } - continue; - } - - if (decl->is_array) { - var_type = process_array_type(&loc, decl_type, decl->array_size, - state); - } else { - var_type = decl_type; - } - - var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto); - - /* From page 22 (page 28 of the PDF) of the GLSL 1.10 specification; - * - * "Global variables can only use the qualifiers const, - * attribute, uni form, or varying. Only one may be - * specified. - * - * Local variables can only use the qualifier const." - * - * This is relaxed in GLSL 1.30. It is also relaxed by any extension - * that adds the 'layout' keyword. - */ - if ((state->language_version < 130) - && !state->ARB_explicit_attrib_location_enable - && !state->ARB_fragment_coord_conventions_enable) { - if (this->type->qualifier.flags.q.out) { - _mesa_glsl_error(& loc, state, - "`out' qualifier in declaration of `%s' " - "only valid for function parameters in %s.", - decl->identifier, state->version_string); - } - if (this->type->qualifier.flags.q.in) { - _mesa_glsl_error(& loc, state, - "`in' qualifier in declaration of `%s' " - "only valid for function parameters in %s.", - decl->identifier, state->version_string); - } - /* FINISHME: Test for other invalid qualifiers. */ - } - - apply_type_qualifier_to_variable(& this->type->qualifier, var, state, - & loc); - - if (this->type->qualifier.flags.q.invariant) { - if ((state->target == vertex_shader) && !(var->mode == ir_var_out || - var->mode == ir_var_inout)) { - /* FINISHME: Note that this doesn't work for invariant on - * a function signature outval - */ - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, vertex shader " - "outputs only\n", var->name); - } else if ((state->target == fragment_shader) && - !(var->mode == ir_var_in || var->mode == ir_var_inout)) { - /* FINISHME: Note that this doesn't work for invariant on - * a function signature inval - */ - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, fragment shader " - "inputs only\n", var->name); - } - } - - if (state->current_function != NULL) { - const char *mode = NULL; - const char *extra = ""; - - /* There is no need to check for 'inout' here because the parser will - * only allow that in function parameter lists. - */ - if (this->type->qualifier.flags.q.attribute) { - mode = "attribute"; - } else if (this->type->qualifier.flags.q.uniform) { - mode = "uniform"; - } else if (this->type->qualifier.flags.q.varying) { - mode = "varying"; - } else if (this->type->qualifier.flags.q.in) { - mode = "in"; - extra = " or in function parameter list"; - } else if (this->type->qualifier.flags.q.out) { - mode = "out"; - extra = " or in function parameter list"; - } - - if (mode) { - _mesa_glsl_error(& loc, state, - "%s variable `%s' must be declared at " - "global scope%s", - mode, var->name, extra); - } - } else if (var->mode == ir_var_in) { - var->read_only = true; - - if (state->target == vertex_shader) { - bool error_emitted = false; - - /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: - * - * "Vertex shader inputs can only be float, floating-point - * vectors, matrices, signed and unsigned integers and integer - * vectors. Vertex shader inputs can also form arrays of these - * types, but not structures." - * - * From page 31 (page 27 of the PDF) of the GLSL 1.30 spec: - * - * "Vertex shader inputs can only be float, floating-point - * vectors, matrices, signed and unsigned integers and integer - * vectors. They cannot be arrays or structures." - * - * From page 23 (page 29 of the PDF) of the GLSL 1.20 spec: - * - * "The attribute qualifier can be used only with float, - * floating-point vectors, and matrices. Attribute variables - * cannot be declared as arrays or structures." - */ - const glsl_type *check_type = var->type->is_array() - ? var->type->fields.array : var->type; - - switch (check_type->base_type) { - case GLSL_TYPE_FLOAT: - break; - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - if (state->language_version > 120) - break; - /* FALLTHROUGH */ - default: - _mesa_glsl_error(& loc, state, - "vertex shader input / attribute cannot have " - "type %s`%s'", - var->type->is_array() ? "array of " : "", - check_type->name); - error_emitted = true; - } - - if (!error_emitted && (state->language_version <= 130) - && var->type->is_array()) { - _mesa_glsl_error(& loc, state, - "vertex shader input / attribute cannot have " - "array type"); - error_emitted = true; - } - } - } - - /* Integer vertex outputs must be qualified with 'flat'. - * - * From section 4.3.6 of the GLSL 1.30 spec: - * "If a vertex output is a signed or unsigned integer or integer - * vector, then it must be qualified with the interpolation qualifier - * flat." - */ - if (state->language_version >= 130 - && state->target == vertex_shader - && state->current_function == NULL - && var->type->is_integer() - && var->mode == ir_var_out - && var->interpolation != ir_var_flat) { - - _mesa_glsl_error(&loc, state, "If a vertex output is an integer, " - "then it must be qualified with 'flat'"); - } - - - /* Interpolation qualifiers cannot be applied to 'centroid' and - * 'centroid varying'. - * - * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: - * "interpolation qualifiers may only precede the qualifiers in, - * centroid in, out, or centroid out in a declaration. They do not apply - * to the deprecated storage qualifiers varying or centroid varying." - */ - if (state->language_version >= 130 - && this->type->qualifier.has_interpolation() - && this->type->qualifier.flags.q.varying) { - - const char *i = this->type->qualifier.interpolation_string(); - assert(i != NULL); - const char *s; - if (this->type->qualifier.flags.q.centroid) - s = "centroid varying"; - else - s = "varying"; - - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to the " - "deprecated storage qualifier '%s'", i, s); - } - - - /* Interpolation qualifiers can only apply to vertex shader outputs and - * fragment shader inputs. - * - * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: - * "Outputs from a vertex shader (out) and inputs to a fragment - * shader (in) can be further qualified with one or more of these - * interpolation qualifiers" - */ - if (state->language_version >= 130 - && this->type->qualifier.has_interpolation()) { - - const char *i = this->type->qualifier.interpolation_string(); - assert(i != NULL); - - switch (state->target) { - case vertex_shader: - if (this->type->qualifier.flags.q.in) { - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to vertex " - "shader inputs", i); - } - break; - case fragment_shader: - if (this->type->qualifier.flags.q.out) { - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to fragment " - "shader outputs", i); - } - break; - default: - assert(0); - } - } - - - /* From section 4.3.4 of the GLSL 1.30 spec: - * "It is an error to use centroid in in a vertex shader." - */ - if (state->language_version >= 130 - && this->type->qualifier.flags.q.centroid - && this->type->qualifier.flags.q.in - && state->target == vertex_shader) { - - _mesa_glsl_error(&loc, state, - "'centroid in' cannot be used in a vertex shader"); - } - - - /* Precision qualifiers exists only in GLSL versions 1.00 and >= 1.30. - */ - if (this->type->specifier->precision != ast_precision_none - && state->language_version != 100 - && state->language_version < 130) { - - _mesa_glsl_error(&loc, state, - "precision qualifiers are supported only in GLSL ES " - "1.00, and GLSL 1.30 and later"); - } - - - /* Precision qualifiers only apply to floating point and integer types. - * - * From section 4.5.2 of the GLSL 1.30 spec: - * "Any floating point or any integer declaration can have the type - * preceded by one of these precision qualifiers [...] Literal - * constants do not have precision qualifiers. Neither do Boolean - * variables. - * - * In GLSL ES, sampler types are also allowed. - * - * From page 87 of the GLSL ES spec: - * "RESOLUTION: Allow sampler types to take a precision qualifier." - */ - if (this->type->specifier->precision != ast_precision_none - && !var->type->is_float() - && !var->type->is_integer() - && !(var->type->is_sampler() && state->es_shader) - && !(var->type->is_array() - && (var->type->fields.array->is_float() - || var->type->fields.array->is_integer()))) { - - _mesa_glsl_error(&loc, state, - "precision qualifiers apply only to floating point" - "%s types", state->es_shader ? ", integer, and sampler" - : "and integer"); - } - - /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: - * - * "[Sampler types] can only be declared as function - * parameters or uniform variables (see Section 4.3.5 - * "Uniform")". - */ - if (var_type->contains_sampler() && - !this->type->qualifier.flags.q.uniform) { - _mesa_glsl_error(&loc, state, "samplers must be declared uniform"); - } - - /* Process the initializer and add its instructions to a temporary - * list. This list will be added to the instruction stream (below) after - * the declaration is added. This is done because in some cases (such as - * redeclarations) the declaration may not actually be added to the - * instruction stream. - */ - exec_list initializer_instructions; - ir_variable *earlier = get_variable_being_redeclared(var, decl, state); - - if (decl->initializer != NULL) { - result = process_initializer((earlier == NULL) ? var : earlier, - decl, this->type, - &initializer_instructions, state); - } - - /* From page 23 (page 29 of the PDF) of the GLSL 1.10 spec: - * - * "It is an error to write to a const variable outside of - * its declaration, so they must be initialized when - * declared." - */ - if (this->type->qualifier.flags.q.constant && decl->initializer == NULL) { - _mesa_glsl_error(& loc, state, - "const declaration of `%s' must be initialized", - decl->identifier); - } - - /* If the declaration is not a redeclaration, there are a few additional - * semantic checks that must be applied. In addition, variable that was - * created for the declaration should be added to the IR stream. - */ - if (earlier == NULL) { - /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, - * - * "Identifiers starting with "gl_" are reserved for use by - * OpenGL, and may not be declared in a shader as either a - * variable or a function." - */ - if (strncmp(decl->identifier, "gl_", 3) == 0) - _mesa_glsl_error(& loc, state, - "identifier `%s' uses reserved `gl_' prefix", - decl->identifier); - - /* Add the variable to the symbol table. Note that the initializer's - * IR was already processed earlier (though it hasn't been emitted - * yet), without the variable in scope. - * - * This differs from most C-like languages, but it follows the GLSL - * specification. From page 28 (page 34 of the PDF) of the GLSL 1.50 - * spec: - * - * "Within a declaration, the scope of a name starts immediately - * after the initializer if present or immediately after the name - * being declared if not." - */ - if (!state->symbols->add_variable(var)) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, "name `%s' already taken in the " - "current scope", decl->identifier); - continue; - } - - /* Push the variable declaration to the top. It means that all the - * variable declarations will appear in a funny last-to-first order, - * but otherwise we run into trouble if a function is prototyped, a - * global var is decled, then the function is defined with usage of - * the global var. See glslparsertest's CorrectModule.frag. - */ - instructions->push_head(var); - } - - instructions->append_list(&initializer_instructions); - } - - - /* Generally, variable declarations do not have r-values. However, - * one is used for the declaration in - * - * while (bool b = some_condition()) { - * ... - * } - * - * so we return the rvalue from the last seen declaration here. - */ - return result; -} - - -ir_rvalue * -ast_parameter_declarator::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - const struct glsl_type *type; - const char *name = NULL; - YYLTYPE loc = this->get_location(); - - type = this->type->specifier->glsl_type(& name, state); - - if (type == NULL) { - if (name != NULL) { - _mesa_glsl_error(& loc, state, - "invalid type `%s' in declaration of `%s'", - name, this->identifier); - } else { - _mesa_glsl_error(& loc, state, - "invalid type in declaration of `%s'", - this->identifier); - } - - type = glsl_type::error_type; - } - - /* From page 62 (page 68 of the PDF) of the GLSL 1.50 spec: - * - * "Functions that accept no input arguments need not use void in the - * argument list because prototypes (or definitions) are required and - * therefore there is no ambiguity when an empty argument list "( )" is - * declared. The idiom "(void)" as a parameter list is provided for - * convenience." - * - * Placing this check here prevents a void parameter being set up - * for a function, which avoids tripping up checks for main taking - * parameters and lookups of an unnamed symbol. - */ - if (type->is_void()) { - if (this->identifier != NULL) - _mesa_glsl_error(& loc, state, - "named parameter cannot have type `void'"); - - is_void = true; - return NULL; - } - - if (formal_parameter && (this->identifier == NULL)) { - _mesa_glsl_error(& loc, state, "formal parameter lacks a name"); - return NULL; - } - - /* This only handles "vec4 foo[..]". The earlier specifier->glsl_type(...) - * call already handled the "vec4[..] foo" case. - */ - if (this->is_array) { - type = process_array_type(&loc, type, this->array_size, state); - } - - if (type->array_size() == 0) { - _mesa_glsl_error(&loc, state, "arrays passed as parameters must have " - "a declared size."); - type = glsl_type::error_type; - } - - is_void = false; - ir_variable *var = new(ctx) ir_variable(type, this->identifier, ir_var_in); - - /* Apply any specified qualifiers to the parameter declaration. Note that - * for function parameters the default mode is 'in'. - */ - apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc); - - /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: - * - * "Samplers cannot be treated as l-values; hence cannot be used - * as out or inout function parameters, nor can they be assigned - * into." - */ - if ((var->mode == ir_var_inout || var->mode == ir_var_out) - && type->contains_sampler()) { - _mesa_glsl_error(&loc, state, "out and inout parameters cannot contain samplers"); - type = glsl_type::error_type; - } - - instructions->push_tail(var); - - /* Parameter declarations do not have r-values. - */ - return NULL; -} - - -void -ast_parameter_declarator::parameters_to_hir(exec_list *ast_parameters, - bool formal, - exec_list *ir_parameters, - _mesa_glsl_parse_state *state) -{ - ast_parameter_declarator *void_param = NULL; - unsigned count = 0; - - foreach_list_typed (ast_parameter_declarator, param, link, ast_parameters) { - param->formal_parameter = formal; - param->hir(ir_parameters, state); - - if (param->is_void) - void_param = param; - - count++; - } - - if ((void_param != NULL) && (count > 1)) { - YYLTYPE loc = void_param->get_location(); - - _mesa_glsl_error(& loc, state, - "`void' parameter must be only parameter"); - } -} - - -void -emit_function(_mesa_glsl_parse_state *state, ir_function *f) -{ - /* IR invariants disallow function declarations or definitions - * nested within other function definitions. But there is no - * requirement about the relative order of function declarations - * and definitions with respect to one another. So simply insert - * the new ir_function block at the end of the toplevel instruction - * list. - */ - state->toplevel_ir->push_tail(f); -} - - -ir_rvalue * -ast_function::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - ir_function *f = NULL; - ir_function_signature *sig = NULL; - exec_list hir_parameters; - - const char *const name = identifier; - - /* New functions are always added to the top-level IR instruction stream, - * so this instruction list pointer is ignored. See also emit_function - * (called below). - */ - (void) instructions; - - /* From page 21 (page 27 of the PDF) of the GLSL 1.20 spec, - * - * "Function declarations (prototypes) cannot occur inside of functions; - * they must be at global scope, or for the built-in functions, outside - * the global scope." - * - * From page 27 (page 33 of the PDF) of the GLSL ES 1.00.16 spec, - * - * "User defined functions may only be defined within the global scope." - * - * Note that this language does not appear in GLSL 1.10. - */ - if ((state->current_function != NULL) && (state->language_version != 110)) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "declaration of function `%s' not allowed within " - "function body", name); - } - - /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, - * - * "Identifiers starting with "gl_" are reserved for use by - * OpenGL, and may not be declared in a shader as either a - * variable or a function." - */ - if (strncmp(name, "gl_", 3) == 0) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "identifier `%s' uses reserved `gl_' prefix", name); - } - - /* Convert the list of function parameters to HIR now so that they can be - * used below to compare this function's signature with previously seen - * signatures for functions with the same name. - */ - ast_parameter_declarator::parameters_to_hir(& this->parameters, - is_definition, - & hir_parameters, state); - - const char *return_type_name; - const glsl_type *return_type = - this->return_type->specifier->glsl_type(& return_type_name, state); - - if (!return_type) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "function `%s' has undeclared return type `%s'", - name, return_type_name); - return_type = glsl_type::error_type; - } - - /* From page 56 (page 62 of the PDF) of the GLSL 1.30 spec: - * "No qualifier is allowed on the return type of a function." - */ - if (this->return_type->has_qualifiers()) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(& loc, state, - "function `%s' return type has qualifiers", name); - } - - /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: - * - * "[Sampler types] can only be declared as function parameters - * or uniform variables (see Section 4.3.5 "Uniform")". - */ - if (return_type->contains_sampler()) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "function `%s' return type can't contain a sampler", - name); - } - - /* Verify that this function's signature either doesn't match a previously - * seen signature for a function with the same name, or, if a match is found, - * that the previously seen signature does not have an associated definition. - */ - f = state->symbols->get_function(name); - if (f != NULL && (state->es_shader || f->has_user_signature())) { - sig = f->exact_matching_signature(&hir_parameters); - if (sig != NULL) { - const char *badvar = sig->qualifiers_match(&hir_parameters); - if (badvar != NULL) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function `%s' parameter `%s' " - "qualifiers don't match prototype", name, badvar); - } - - if (sig->return_type != return_type) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function `%s' return type doesn't " - "match prototype", name); - } - - if (is_definition && sig->is_defined) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "function `%s' redefined", name); - } - } - } else { - f = new(ctx) ir_function(name); - if (!state->symbols->add_function(f)) { - /* This function name shadows a non-function use of the same name. */ - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function name `%s' conflicts with " - "non-function", name); - return NULL; - } - - emit_function(state, f); - } - - /* Verify the return type of main() */ - if (strcmp(name, "main") == 0) { - if (! return_type->is_void()) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "main() must return void"); - } - - if (!hir_parameters.is_empty()) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "main() must not take any parameters"); - } - } - - /* Finish storing the information about this new function in its signature. - */ - if (sig == NULL) { - sig = new(ctx) ir_function_signature(return_type); - f->add_signature(sig); - } - - sig->replace_parameters(&hir_parameters); - signature = sig; - - /* Function declarations (prototypes) do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_function_definition::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - prototype->is_definition = true; - prototype->hir(instructions, state); - - ir_function_signature *signature = prototype->signature; - if (signature == NULL) - return NULL; - - assert(state->current_function == NULL); - state->current_function = signature; - state->found_return = false; - - /* Duplicate parameters declared in the prototype as concrete variables. - * Add these to the symbol table. - */ - state->symbols->push_scope(); - foreach_iter(exec_list_iterator, iter, signature->parameters) { - ir_variable *const var = ((ir_instruction *) iter.get())->as_variable(); - - assert(var != NULL); - - /* The only way a parameter would "exist" is if two parameters have - * the same name. - */ - if (state->symbols->name_declared_this_scope(var->name)) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "parameter `%s' redeclared", var->name); - } else { - state->symbols->add_variable(var); - } - } - - /* Convert the body of the function to HIR. */ - this->body->hir(&signature->body, state); - signature->is_defined = true; - - state->symbols->pop_scope(); - - assert(state->current_function == signature); - state->current_function = NULL; - - if (!signature->return_type->is_void() && !state->found_return) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(& loc, state, "function `%s' has non-void return type " - "%s, but no return statement", - signature->function_name(), - signature->return_type->name); - } - - /* Function definitions do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_jump_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - switch (mode) { - case ast_return: { - ir_return *inst; - assert(state->current_function); - - if (opt_return_value) { - ir_rvalue *const ret = opt_return_value->hir(instructions, state); - - /* The value of the return type can be NULL if the shader says - * 'return foo();' and foo() is a function that returns void. - * - * NOTE: The GLSL spec doesn't say that this is an error. The type - * of the return value is void. If the return type of the function is - * also void, then this should compile without error. Seriously. - */ - const glsl_type *const ret_type = - (ret == NULL) ? glsl_type::void_type : ret->type; - - /* Implicit conversions are not allowed for return values. */ - if (state->current_function->return_type != ret_type) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`return' with wrong type %s, in function `%s' " - "returning %s", - ret_type->name, - state->current_function->function_name(), - state->current_function->return_type->name); - } - - inst = new(ctx) ir_return(ret); - } else { - if (state->current_function->return_type->base_type != - GLSL_TYPE_VOID) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`return' with no value, in function %s returning " - "non-void", - state->current_function->function_name()); - } - inst = new(ctx) ir_return; - } - - state->found_return = true; - instructions->push_tail(inst); - break; - } - - case ast_discard: - if (state->target != fragment_shader) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`discard' may only appear in a fragment shader"); - } - instructions->push_tail(new(ctx) ir_discard); - break; - - case ast_break: - case ast_continue: - /* FINISHME: Handle switch-statements. They cannot contain 'continue', - * FINISHME: and they use a different IR instruction for 'break'. - */ - /* FINISHME: Correctly handle the nesting. If a switch-statement is - * FINISHME: inside a loop, a 'continue' is valid and will bind to the - * FINISHME: loop. - */ - if (state->loop_or_switch_nesting == NULL) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`%s' may only appear in a loop", - (mode == ast_break) ? "break" : "continue"); - } else { - ir_loop *const loop = state->loop_or_switch_nesting->as_loop(); - - /* Inline the for loop expression again, since we don't know - * where near the end of the loop body the normal copy of it - * is going to be placed. - */ - if (mode == ast_continue && - state->loop_or_switch_nesting_ast->rest_expression) { - state->loop_or_switch_nesting_ast->rest_expression->hir(instructions, - state); - } - - if (loop != NULL) { - ir_loop_jump *const jump = - new(ctx) ir_loop_jump((mode == ast_break) - ? ir_loop_jump::jump_break - : ir_loop_jump::jump_continue); - instructions->push_tail(jump); - } - } - - break; - } - - /* Jump instructions do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_selection_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - ir_rvalue *const condition = this->condition->hir(instructions, state); - - /* From page 66 (page 72 of the PDF) of the GLSL 1.50 spec: - * - * "Any expression whose type evaluates to a Boolean can be used as the - * conditional expression bool-expression. Vector types are not accepted - * as the expression to if." - * - * The checks are separated so that higher quality diagnostics can be - * generated for cases where both rules are violated. - */ - if (!condition->type->is_boolean() || !condition->type->is_scalar()) { - YYLTYPE loc = this->condition->get_location(); - - _mesa_glsl_error(& loc, state, "if-statement condition must be scalar " - "boolean"); - } - - ir_if *const stmt = new(ctx) ir_if(condition); - - if (then_statement != NULL) { - state->symbols->push_scope(); - then_statement->hir(& stmt->then_instructions, state); - state->symbols->pop_scope(); - } - - if (else_statement != NULL) { - state->symbols->push_scope(); - else_statement->hir(& stmt->else_instructions, state); - state->symbols->pop_scope(); - } - - instructions->push_tail(stmt); - - /* if-statements do not have r-values. - */ - return NULL; -} - - -void -ast_iteration_statement::condition_to_hir(ir_loop *stmt, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - if (condition != NULL) { - ir_rvalue *const cond = - condition->hir(& stmt->body_instructions, state); - - if ((cond == NULL) - || !cond->type->is_boolean() || !cond->type->is_scalar()) { - YYLTYPE loc = condition->get_location(); - - _mesa_glsl_error(& loc, state, - "loop condition must be scalar boolean"); - } else { - /* As the first code in the loop body, generate a block that looks - * like 'if (!condition) break;' as the loop termination condition. - */ - ir_rvalue *const not_cond = - new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond, - NULL); - - ir_if *const if_stmt = new(ctx) ir_if(not_cond); - - ir_jump *const break_stmt = - new(ctx) ir_loop_jump(ir_loop_jump::jump_break); - - if_stmt->then_instructions.push_tail(break_stmt); - stmt->body_instructions.push_tail(if_stmt); - } - } -} - - -ir_rvalue * -ast_iteration_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - /* For-loops and while-loops start a new scope, but do-while loops do not. - */ - if (mode != ast_do_while) - state->symbols->push_scope(); - - if (init_statement != NULL) - init_statement->hir(instructions, state); - - ir_loop *const stmt = new(ctx) ir_loop(); - instructions->push_tail(stmt); - - /* Track the current loop and / or switch-statement nesting. - */ - ir_instruction *const nesting = state->loop_or_switch_nesting; - ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast; - - state->loop_or_switch_nesting = stmt; - state->loop_or_switch_nesting_ast = this; - - if (mode != ast_do_while) - condition_to_hir(stmt, state); - - if (body != NULL) - body->hir(& stmt->body_instructions, state); - - if (rest_expression != NULL) - rest_expression->hir(& stmt->body_instructions, state); - - if (mode == ast_do_while) - condition_to_hir(stmt, state); - - if (mode != ast_do_while) - state->symbols->pop_scope(); - - /* Restore previous nesting before returning. - */ - state->loop_or_switch_nesting = nesting; - state->loop_or_switch_nesting_ast = nesting_ast; - - /* Loops do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_type_specifier::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - if (!this->is_precision_statement && this->structure == NULL) - return NULL; - - YYLTYPE loc = this->get_location(); - - if (this->precision != ast_precision_none - && state->language_version != 100 - && state->language_version < 130) { - _mesa_glsl_error(&loc, state, - "precision qualifiers exist only in " - "GLSL ES 1.00, and GLSL 1.30 and later"); - return NULL; - } - if (this->precision != ast_precision_none - && this->structure != NULL) { - _mesa_glsl_error(&loc, state, - "precision qualifiers do not apply to structures"); - return NULL; - } - - /* If this is a precision statement, check that the type to which it is - * applied is either float or int. - * - * From section 4.5.3 of the GLSL 1.30 spec: - * "The precision statement - * precision precision-qualifier type; - * can be used to establish a default precision qualifier. The type - * field can be either int or float [...]. Any other types or - * qualifiers will result in an error. - */ - if (this->is_precision_statement) { - assert(this->precision != ast_precision_none); - assert(this->structure == NULL); /* The check for structures was - * performed above. */ - if (this->is_array) { - _mesa_glsl_error(&loc, state, - "default precision statements do not apply to " - "arrays"); - return NULL; - } - if (this->type_specifier != ast_float - && this->type_specifier != ast_int) { - _mesa_glsl_error(&loc, state, - "default precision statements apply only to types " - "float and int"); - return NULL; - } - - /* FINISHME: Translate precision statements into IR. */ - return NULL; - } - - if (this->structure != NULL) - return this->structure->hir(instructions, state); - - return NULL; -} - - -ir_rvalue * -ast_struct_specifier::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - unsigned decl_count = 0; - - /* Make an initial pass over the list of structure fields to determine how - * many there are. Each element in this list is an ast_declarator_list. - * This means that we actually need to count the number of elements in the - * 'declarations' list in each of the elements. - */ - foreach_list_typed (ast_declarator_list, decl_list, link, - &this->declarations) { - foreach_list_const (decl_ptr, & decl_list->declarations) { - decl_count++; - } - } - - /* Allocate storage for the structure fields and process the field - * declarations. As the declarations are processed, try to also convert - * the types to HIR. This ensures that structure definitions embedded in - * other structure definitions are processed. - */ - glsl_struct_field *const fields = ralloc_array(state, glsl_struct_field, - decl_count); - - unsigned i = 0; - foreach_list_typed (ast_declarator_list, decl_list, link, - &this->declarations) { - const char *type_name; - - decl_list->type->specifier->hir(instructions, state); - - /* Section 10.9 of the GLSL ES 1.00 specification states that - * embedded structure definitions have been removed from the language. - */ - if (state->es_shader && decl_list->type->specifier->structure != NULL) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, "Embedded structure definitions are " - "not allowed in GLSL ES 1.00."); - } - - const glsl_type *decl_type = - decl_list->type->specifier->glsl_type(& type_name, state); - - foreach_list_typed (ast_declaration, decl, link, - &decl_list->declarations) { - const struct glsl_type *field_type = decl_type; - if (decl->is_array) { - YYLTYPE loc = decl->get_location(); - field_type = process_array_type(&loc, decl_type, decl->array_size, - state); - } - fields[i].type = (field_type != NULL) - ? field_type : glsl_type::error_type; - fields[i].name = decl->identifier; - i++; - } - } - - assert(i == decl_count); - - const glsl_type *t = - glsl_type::get_record_instance(fields, decl_count, this->name); - - YYLTYPE loc = this->get_location(); - if (!state->symbols->add_type(name, t)) { - _mesa_glsl_error(& loc, state, "struct `%s' previously defined", name); - } else { - const glsl_type **s = reralloc(state, state->user_structures, - const glsl_type *, - state->num_user_structures + 1); - if (s != NULL) { - s[state->num_user_structures] = t; - state->user_structures = s; - state->num_user_structures++; - } - } - - /* Structure type definitions do not have r-values. - */ - return NULL; -} +/* + * 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. + */ + +/** + * \file ast_to_hir.c + * Convert abstract syntax to to high-level intermediate reprensentation (HIR). + * + * During the conversion to HIR, the majority of the symantic checking is + * preformed on the program. This includes: + * + * * Symbol table management + * * Type checking + * * Function binding + * + * The majority of this work could be done during parsing, and the parser could + * probably generate HIR directly. However, this results in frequent changes + * to the parser code. Since we do not assume that every system this complier + * is built on will have Flex and Bison installed, we have to store the code + * generated by these tools in our version control system. In other parts of + * the system we've seen problems where a parser was changed but the generated + * code was not committed, merge conflicts where created because two developers + * had slightly different versions of Bison installed, etc. + * + * I have also noticed that running Bison generated parsers in GDB is very + * irritating. When you get a segfault on '$$ = $1->foo', you can't very + * well 'print $1' in GDB. + * + * As a result, my preference is to put as little C code as possible in the + * parser (and lexer) sources. + */ + +#include "main/core.h" /* for struct gl_extensions */ +#include "glsl_symbol_table.h" +#include "glsl_parser_extras.h" +#include "ast.h" +#include "glsl_types.h" +#include "ir.h" + +void +_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) +{ + _mesa_glsl_initialize_variables(instructions, state); + _mesa_glsl_initialize_functions(state); + + state->symbols->language_version = state->language_version; + + state->current_function = NULL; + + state->toplevel_ir = instructions; + + /* Section 4.2 of the GLSL 1.20 specification states: + * "The built-in functions are scoped in a scope outside the global scope + * users declare global variables in. That is, a shader's global scope, + * available for user-defined functions and global variables, is nested + * inside the scope containing the built-in functions." + * + * Since built-in functions like ftransform() access built-in variables, + * it follows that those must be in the outer scope as well. + * + * We push scope here to create this nesting effect...but don't pop. + * This way, a shader's globals are still in the symbol table for use + * by the linker. + */ + state->symbols->push_scope(); + + foreach_list_typed (ast_node, ast, link, & state->translation_unit) + ast->hir(instructions, state); + + detect_recursion_unlinked(state, instructions); + + state->toplevel_ir = NULL; +} + + +/** + * If a conversion is available, convert one operand to a different type + * + * The \c from \c ir_rvalue is converted "in place". + * + * \param to Type that the operand it to be converted to + * \param from Operand that is being converted + * \param state GLSL compiler state + * + * \return + * If a conversion is possible (or unnecessary), \c true is returned. + * Otherwise \c false is returned. + */ +bool +apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + if (to->base_type == from->type->base_type) + return true; + + /* This conversion was added in GLSL 1.20. If the compilation mode is + * GLSL 1.10, the conversion is skipped. + */ + if (state->language_version < 120) + return false; + + /* From page 27 (page 33 of the PDF) of the GLSL 1.50 spec: + * + * "There are no implicit array or structure conversions. For + * example, an array of int cannot be implicitly converted to an + * array of float. There are no implicit conversions between + * signed and unsigned integers." + */ + /* FINISHME: The above comment is partially a lie. There is int/uint + * FINISHME: conversion for immediate constants. + */ + if (!to->is_float() || !from->type->is_numeric()) + return false; + + /* Convert to a floating point type with the same number of components + * as the original type - i.e. int to float, not int to vec4. + */ + to = glsl_type::get_instance(GLSL_TYPE_FLOAT, from->type->vector_elements, + from->type->matrix_columns); + + switch (from->type->base_type) { + case GLSL_TYPE_INT: + from = new(ctx) ir_expression(ir_unop_i2f, to, from, NULL); + break; + case GLSL_TYPE_UINT: + from = new(ctx) ir_expression(ir_unop_u2f, to, from, NULL); + break; + case GLSL_TYPE_BOOL: + from = new(ctx) ir_expression(ir_unop_b2f, to, from, NULL); + break; + default: + assert(0); + } + + return true; +} + + +static const struct glsl_type * +arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, + bool multiply, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + const glsl_type *type_a = value_a->type; + const glsl_type *type_b = value_b->type; + + /* From GLSL 1.50 spec, page 56: + * + * "The arithmetic binary operators add (+), subtract (-), + * multiply (*), and divide (/) operate on integer and + * floating-point scalars, vectors, and matrices." + */ + if (!type_a->is_numeric() || !type_b->is_numeric()) { + _mesa_glsl_error(loc, state, + "Operands to arithmetic operators must be numeric"); + return glsl_type::error_type; + } + + + /* "If one operand is floating-point based and the other is + * not, then the conversions from Section 4.1.10 "Implicit + * Conversions" are applied to the non-floating-point-based operand." + */ + if (!apply_implicit_conversion(type_a, value_b, state) + && !apply_implicit_conversion(type_b, value_a, state)) { + _mesa_glsl_error(loc, state, + "Could not implicitly convert operands to " + "arithmetic operator"); + return glsl_type::error_type; + } + type_a = value_a->type; + type_b = value_b->type; + + /* "If the operands are integer types, they must both be signed or + * both be unsigned." + * + * From this rule and the preceeding conversion it can be inferred that + * both types must be GLSL_TYPE_FLOAT, or GLSL_TYPE_UINT, or GLSL_TYPE_INT. + * The is_numeric check above already filtered out the case where either + * type is not one of these, so now the base types need only be tested for + * equality. + */ + if (type_a->base_type != type_b->base_type) { + _mesa_glsl_error(loc, state, + "base type mismatch for arithmetic operator"); + return glsl_type::error_type; + } + + /* "All arithmetic binary operators result in the same fundamental type + * (signed integer, unsigned integer, or floating-point) as the + * operands they operate on, after operand type conversion. After + * conversion, the following cases are valid + * + * * The two operands are scalars. In this case the operation is + * applied, resulting in a scalar." + */ + if (type_a->is_scalar() && type_b->is_scalar()) + return type_a; + + /* "* One operand is a scalar, and the other is a vector or matrix. + * In this case, the scalar operation is applied independently to each + * component of the vector or matrix, resulting in the same size + * vector or matrix." + */ + if (type_a->is_scalar()) { + if (!type_b->is_scalar()) + return type_b; + } else if (type_b->is_scalar()) { + return type_a; + } + + /* All of the combinations of , , + * , , and have been + * handled. + */ + assert(!type_a->is_scalar()); + assert(!type_b->is_scalar()); + + /* "* The two operands are vectors of the same size. In this case, the + * operation is done component-wise resulting in the same size + * vector." + */ + if (type_a->is_vector() && type_b->is_vector()) { + if (type_a == type_b) { + return type_a; + } else { + _mesa_glsl_error(loc, state, + "vector size mismatch for arithmetic operator"); + return glsl_type::error_type; + } + } + + /* All of the combinations of , , + * , , , and + * have been handled. At least one of the operands must + * be matrix. Further, since there are no integer matrix types, the base + * type of both operands must be float. + */ + assert(type_a->is_matrix() || type_b->is_matrix()); + assert(type_a->base_type == GLSL_TYPE_FLOAT); + assert(type_b->base_type == GLSL_TYPE_FLOAT); + + /* "* The operator is add (+), subtract (-), or divide (/), and the + * operands are matrices with the same number of rows and the same + * number of columns. In this case, the operation is done component- + * wise resulting in the same size matrix." + * * The operator is multiply (*), where both operands are matrices or + * one operand is a vector and the other a matrix. A right vector + * operand is treated as a column vector and a left vector operand as a + * row vector. In all these cases, it is required that the number of + * columns of the left operand is equal to the number of rows of the + * right operand. Then, the multiply (*) operation does a linear + * algebraic multiply, yielding an object that has the same number of + * rows as the left operand and the same number of columns as the right + * operand. Section 5.10 "Vector and Matrix Operations" explains in + * more detail how vectors and matrices are operated on." + */ + if (! multiply) { + if (type_a == type_b) + return type_a; + } else { + if (type_a->is_matrix() && type_b->is_matrix()) { + /* Matrix multiply. The columns of A must match the rows of B. Given + * the other previously tested constraints, this means the vector type + * of a row from A must be the same as the vector type of a column from + * B. + */ + if (type_a->row_type() == type_b->column_type()) { + /* The resulting matrix has the number of columns of matrix B and + * the number of rows of matrix A. We get the row count of A by + * looking at the size of a vector that makes up a column. The + * transpose (size of a row) is done for B. + */ + const glsl_type *const type = + glsl_type::get_instance(type_a->base_type, + type_a->column_type()->vector_elements, + type_b->row_type()->vector_elements); + assert(type != glsl_type::error_type); + + return type; + } + } else if (type_a->is_matrix()) { + /* A is a matrix and B is a column vector. Columns of A must match + * rows of B. Given the other previously tested constraints, this + * means the vector type of a row from A must be the same as the + * vector the type of B. + */ + if (type_a->row_type() == type_b) { + /* The resulting vector has a number of elements equal to + * the number of rows of matrix A. */ + const glsl_type *const type = + glsl_type::get_instance(type_a->base_type, + type_a->column_type()->vector_elements, + 1); + assert(type != glsl_type::error_type); + + return type; + } + } else { + assert(type_b->is_matrix()); + + /* A is a row vector and B is a matrix. Columns of A must match rows + * of B. Given the other previously tested constraints, this means + * the type of A must be the same as the vector type of a column from + * B. + */ + if (type_a == type_b->column_type()) { + /* The resulting vector has a number of elements equal to + * the number of columns of matrix B. */ + const glsl_type *const type = + glsl_type::get_instance(type_a->base_type, + type_b->row_type()->vector_elements, + 1); + assert(type != glsl_type::error_type); + + return type; + } + } + + _mesa_glsl_error(loc, state, "size mismatch for matrix multiplication"); + return glsl_type::error_type; + } + + + /* "All other cases are illegal." + */ + _mesa_glsl_error(loc, state, "type mismatch"); + return glsl_type::error_type; +} + + +static const struct glsl_type * +unary_arithmetic_result_type(const struct glsl_type *type, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + /* From GLSL 1.50 spec, page 57: + * + * "The arithmetic unary operators negate (-), post- and pre-increment + * and decrement (-- and ++) operate on integer or floating-point + * values (including vectors and matrices). All unary operators work + * component-wise on their operands. These result with the same type + * they operated on." + */ + if (!type->is_numeric()) { + _mesa_glsl_error(loc, state, + "Operands to arithmetic operators must be numeric"); + return glsl_type::error_type; + } + + return type; +} + +/** + * \brief Return the result type of a bit-logic operation. + * + * If the given types to the bit-logic operator are invalid, return + * glsl_type::error_type. + * + * \param type_a Type of LHS of bit-logic op + * \param type_b Type of RHS of bit-logic op + */ +static const struct glsl_type * +bit_logic_result_type(const struct glsl_type *type_a, + const struct glsl_type *type_b, + ast_operators op, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + if (state->language_version < 130) { + _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); + return glsl_type::error_type; + } + + /* From page 50 (page 56 of PDF) of GLSL 1.30 spec: + * + * "The bitwise operators and (&), exclusive-or (^), and inclusive-or + * (|). The operands must be of type signed or unsigned integers or + * integer vectors." + */ + if (!type_a->is_integer()) { + _mesa_glsl_error(loc, state, "LHS of `%s' must be an integer", + ast_expression::operator_string(op)); + return glsl_type::error_type; + } + if (!type_b->is_integer()) { + _mesa_glsl_error(loc, state, "RHS of `%s' must be an integer", + ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* "The fundamental types of the operands (signed or unsigned) must + * match," + */ + if (type_a->base_type != type_b->base_type) { + _mesa_glsl_error(loc, state, "operands of `%s' must have the same " + "base type", ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* "The operands cannot be vectors of differing size." */ + if (type_a->is_vector() && + type_b->is_vector() && + type_a->vector_elements != type_b->vector_elements) { + _mesa_glsl_error(loc, state, "operands of `%s' cannot be vectors of " + "different sizes", ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* "If one operand is a scalar and the other a vector, the scalar is + * applied component-wise to the vector, resulting in the same type as + * the vector. The fundamental types of the operands [...] will be the + * resulting fundamental type." + */ + if (type_a->is_scalar()) + return type_b; + else + return type_a; +} + +static const struct glsl_type * +modulus_result_type(const struct glsl_type *type_a, + const struct glsl_type *type_b, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + if (state->language_version < 130) { + _mesa_glsl_error(loc, state, + "operator '%%' is reserved in %s", + state->version_string); + return glsl_type::error_type; + } + + /* From GLSL 1.50 spec, page 56: + * "The operator modulus (%) operates on signed or unsigned integers or + * integer vectors. The operand types must both be signed or both be + * unsigned." + */ + if (!type_a->is_integer()) { + _mesa_glsl_error(loc, state, "LHS of operator %% must be an integer."); + return glsl_type::error_type; + } + if (!type_b->is_integer()) { + _mesa_glsl_error(loc, state, "RHS of operator %% must be an integer."); + return glsl_type::error_type; + } + if (type_a->base_type != type_b->base_type) { + _mesa_glsl_error(loc, state, + "operands of %% must have the same base type"); + return glsl_type::error_type; + } + + /* "The operands cannot be vectors of differing size. If one operand is + * a scalar and the other vector, then the scalar is applied component- + * wise to the vector, resulting in the same type as the vector. If both + * are vectors of the same size, the result is computed component-wise." + */ + if (type_a->is_vector()) { + if (!type_b->is_vector() + || (type_a->vector_elements == type_b->vector_elements)) + return type_a; + } else + return type_b; + + /* "The operator modulus (%) is not defined for any other data types + * (non-integer types)." + */ + _mesa_glsl_error(loc, state, "type mismatch"); + return glsl_type::error_type; +} + + +static const struct glsl_type * +relational_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + const glsl_type *type_a = value_a->type; + const glsl_type *type_b = value_b->type; + + /* From GLSL 1.50 spec, page 56: + * "The relational operators greater than (>), less than (<), greater + * than or equal (>=), and less than or equal (<=) operate only on + * scalar integer and scalar floating-point expressions." + */ + if (!type_a->is_numeric() + || !type_b->is_numeric() + || !type_a->is_scalar() + || !type_b->is_scalar()) { + _mesa_glsl_error(loc, state, + "Operands to relational operators must be scalar and " + "numeric"); + return glsl_type::error_type; + } + + /* "Either the operands' types must match, or the conversions from + * Section 4.1.10 "Implicit Conversions" will be applied to the integer + * operand, after which the types must match." + */ + if (!apply_implicit_conversion(type_a, value_b, state) + && !apply_implicit_conversion(type_b, value_a, state)) { + _mesa_glsl_error(loc, state, + "Could not implicitly convert operands to " + "relational operator"); + return glsl_type::error_type; + } + type_a = value_a->type; + type_b = value_b->type; + + if (type_a->base_type != type_b->base_type) { + _mesa_glsl_error(loc, state, "base type mismatch"); + return glsl_type::error_type; + } + + /* "The result is scalar Boolean." + */ + return glsl_type::bool_type; +} + +/** + * \brief Return the result type of a bit-shift operation. + * + * If the given types to the bit-shift operator are invalid, return + * glsl_type::error_type. + * + * \param type_a Type of LHS of bit-shift op + * \param type_b Type of RHS of bit-shift op + */ +static const struct glsl_type * +shift_result_type(const struct glsl_type *type_a, + const struct glsl_type *type_b, + ast_operators op, + struct _mesa_glsl_parse_state *state, YYLTYPE *loc) +{ + if (state->language_version < 130) { + _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); + return glsl_type::error_type; + } + + /* From page 50 (page 56 of the PDF) of the GLSL 1.30 spec: + * + * "The shift operators (<<) and (>>). For both operators, the operands + * must be signed or unsigned integers or integer vectors. One operand + * can be signed while the other is unsigned." + */ + if (!type_a->is_integer()) { + _mesa_glsl_error(loc, state, "LHS of operator %s must be an integer or " + "integer vector", ast_expression::operator_string(op)); + return glsl_type::error_type; + + } + if (!type_b->is_integer()) { + _mesa_glsl_error(loc, state, "RHS of operator %s must be an integer or " + "integer vector", ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* "If the first operand is a scalar, the second operand has to be + * a scalar as well." + */ + if (type_a->is_scalar() && !type_b->is_scalar()) { + _mesa_glsl_error(loc, state, "If the first operand of %s is scalar, the " + "second must be scalar as well", + ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* If both operands are vectors, check that they have same number of + * elements. + */ + if (type_a->is_vector() && + type_b->is_vector() && + type_a->vector_elements != type_b->vector_elements) { + _mesa_glsl_error(loc, state, "Vector operands to operator %s must " + "have same number of elements", + ast_expression::operator_string(op)); + return glsl_type::error_type; + } + + /* "In all cases, the resulting type will be the same type as the left + * operand." + */ + return type_a; +} + +/** + * Validates that a value can be assigned to a location with a specified type + * + * Validates that \c rhs can be assigned to some location. If the types are + * not an exact match but an automatic conversion is possible, \c rhs will be + * converted. + * + * \return + * \c NULL if \c rhs cannot be assigned to a location with type \c lhs_type. + * Otherwise the actual RHS to be assigned will be returned. This may be + * \c rhs, or it may be \c rhs after some type conversion. + * + * \note + * In addition to being used for assignments, this function is used to + * type-check return values. + */ +ir_rvalue * +validate_assignment(struct _mesa_glsl_parse_state *state, + const glsl_type *lhs_type, ir_rvalue *rhs, + bool is_initializer) +{ + /* If there is already some error in the RHS, just return it. Anything + * else will lead to an avalanche of error message back to the user. + */ + if (rhs->type->is_error()) + return rhs; + + /* If the types are identical, the assignment can trivially proceed. + */ + if (rhs->type == lhs_type) + return rhs; + + /* If the array element types are the same and the size of the LHS is zero, + * the assignment is okay for initializers embedded in variable + * declarations. + * + * Note: Whole-array assignments are not permitted in GLSL 1.10, but this + * is handled by ir_dereference::is_lvalue. + */ + if (is_initializer && lhs_type->is_array() && rhs->type->is_array() + && (lhs_type->element_type() == rhs->type->element_type()) + && (lhs_type->array_size() == 0)) { + return rhs; + } + + /* Check for implicit conversion in GLSL 1.20 */ + if (apply_implicit_conversion(lhs_type, rhs, state)) { + if (rhs->type == lhs_type) + return rhs; + } + + return NULL; +} + +static void +mark_whole_array_access(ir_rvalue *access) +{ + ir_dereference_variable *deref = access->as_dereference_variable(); + + if (deref && deref->var) { + deref->var->max_array_access = deref->type->length - 1; + } +} + +ir_rvalue * +do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state, + ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer, + YYLTYPE lhs_loc) +{ + void *ctx = state; + bool error_emitted = (lhs->type->is_error() || rhs->type->is_error()); + + if (!error_emitted) { + if (lhs->variable_referenced() != NULL + && lhs->variable_referenced()->read_only) { + _mesa_glsl_error(&lhs_loc, state, + "assignment to read-only variable '%s'", + lhs->variable_referenced()->name); + error_emitted = true; + + } else if (state->language_version <= 110 && lhs->type->is_array()) { + /* From page 32 (page 38 of the PDF) of the GLSL 1.10 spec: + * + * "Other binary or unary expressions, non-dereferenced + * arrays, function names, swizzles with repeated fields, + * and constants cannot be l-values." + */ + _mesa_glsl_error(&lhs_loc, state, "whole array assignment is not " + "allowed in GLSL 1.10 or GLSL ES 1.00."); + error_emitted = true; + } else if (!lhs->is_lvalue()) { + _mesa_glsl_error(& lhs_loc, state, "non-lvalue in assignment"); + error_emitted = true; + } + } + + ir_rvalue *new_rhs = + validate_assignment(state, lhs->type, rhs, is_initializer); + if (new_rhs == NULL) { + _mesa_glsl_error(& lhs_loc, state, "type mismatch"); + } else { + rhs = new_rhs; + + /* If the LHS array was not declared with a size, it takes it size from + * the RHS. If the LHS is an l-value and a whole array, it must be a + * dereference of a variable. Any other case would require that the LHS + * is either not an l-value or not a whole array. + */ + if (lhs->type->array_size() == 0) { + ir_dereference *const d = lhs->as_dereference(); + + assert(d != NULL); + + ir_variable *const var = d->variable_referenced(); + + assert(var != NULL); + + if (var->max_array_access >= unsigned(rhs->type->array_size())) { + /* FINISHME: This should actually log the location of the RHS. */ + _mesa_glsl_error(& lhs_loc, state, "array size must be > %u due to " + "previous access", + var->max_array_access); + } + + var->type = glsl_type::get_array_instance(lhs->type->element_type(), + rhs->type->array_size()); + d->type = var->type; + } + mark_whole_array_access(rhs); + mark_whole_array_access(lhs); + } + + /* Most callers of do_assignment (assign, add_assign, pre_inc/dec, + * but not post_inc) need the converted assigned value as an rvalue + * to handle things like: + * + * i = j += 1; + * + * So we always just store the computed value being assigned to a + * temporary and return a deref of that temporary. If the rvalue + * ends up not being used, the temp will get copy-propagated out. + */ + ir_variable *var = new(ctx) ir_variable(rhs->type, "assignment_tmp", + ir_var_temporary); + ir_dereference_variable *deref_var = new(ctx) ir_dereference_variable(var); + instructions->push_tail(var); + instructions->push_tail(new(ctx) ir_assignment(deref_var, + rhs, + NULL)); + deref_var = new(ctx) ir_dereference_variable(var); + + if (!error_emitted) + instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var, NULL)); + + return new(ctx) ir_dereference_variable(var); +} + +static ir_rvalue * +get_lvalue_copy(exec_list *instructions, ir_rvalue *lvalue) +{ + void *ctx = ralloc_parent(lvalue); + ir_variable *var; + + var = new(ctx) ir_variable(lvalue->type, "_post_incdec_tmp", + ir_var_temporary); + instructions->push_tail(var); + var->mode = ir_var_auto; + + instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var), + lvalue, NULL)); + + /* Once we've created this temporary, mark it read only so it's no + * longer considered an lvalue. + */ + var->read_only = true; + + return new(ctx) ir_dereference_variable(var); +} + + +ir_rvalue * +ast_node::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + (void) instructions; + (void) state; + + return NULL; +} + +static ir_rvalue * +do_comparison(void *mem_ctx, int operation, ir_rvalue *op0, ir_rvalue *op1) +{ + int join_op; + ir_rvalue *cmp = NULL; + + if (operation == ir_binop_all_equal) + join_op = ir_binop_logic_and; + else + join_op = ir_binop_logic_or; + + switch (op0->type->base_type) { + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_BOOL: + return new(mem_ctx) ir_expression(operation, op0, op1); + + case GLSL_TYPE_ARRAY: { + for (unsigned int i = 0; i < op0->type->length; i++) { + ir_rvalue *e0, *e1, *result; + + e0 = new(mem_ctx) ir_dereference_array(op0->clone(mem_ctx, NULL), + new(mem_ctx) ir_constant(i)); + e1 = new(mem_ctx) ir_dereference_array(op1->clone(mem_ctx, NULL), + new(mem_ctx) ir_constant(i)); + result = do_comparison(mem_ctx, operation, e0, e1); + + if (cmp) { + cmp = new(mem_ctx) ir_expression(join_op, cmp, result); + } else { + cmp = result; + } + } + + mark_whole_array_access(op0); + mark_whole_array_access(op1); + break; + } + + case GLSL_TYPE_STRUCT: { + for (unsigned int i = 0; i < op0->type->length; i++) { + ir_rvalue *e0, *e1, *result; + const char *field_name = op0->type->fields.structure[i].name; + + e0 = new(mem_ctx) ir_dereference_record(op0->clone(mem_ctx, NULL), + field_name); + e1 = new(mem_ctx) ir_dereference_record(op1->clone(mem_ctx, NULL), + field_name); + result = do_comparison(mem_ctx, operation, e0, e1); + + if (cmp) { + cmp = new(mem_ctx) ir_expression(join_op, cmp, result); + } else { + cmp = result; + } + } + break; + } + + case GLSL_TYPE_ERROR: + case GLSL_TYPE_VOID: + case GLSL_TYPE_SAMPLER: + /* I assume a comparison of a struct containing a sampler just + * ignores the sampler present in the type. + */ + break; + + default: + assert(!"Should not get here."); + break; + } + + if (cmp == NULL) + cmp = new(mem_ctx) ir_constant(true); + + return cmp; +} + +/* For logical operations, we want to ensure that the operands are + * scalar booleans. If it isn't, emit an error and return a constant + * boolean to avoid triggering cascading error messages. + */ +ir_rvalue * +get_scalar_boolean_operand(exec_list *instructions, + struct _mesa_glsl_parse_state *state, + ast_expression *parent_expr, + int operand, + const char *operand_name, + bool *error_emitted) +{ + ast_expression *expr = parent_expr->subexpressions[operand]; + void *ctx = state; + ir_rvalue *val = expr->hir(instructions, state); + + if (val->type->is_boolean() && val->type->is_scalar()) + return val; + + if (!*error_emitted) { + YYLTYPE loc = expr->get_location(); + _mesa_glsl_error(&loc, state, "%s of `%s' must be scalar boolean", + operand_name, + parent_expr->operator_string(parent_expr->oper)); + *error_emitted = true; + } + + return new(ctx) ir_constant(true); +} + +/** + * If name refers to a builtin array whose maximum allowed size is less than + * size, report an error and return true. Otherwise return false. + */ +static bool +check_builtin_array_max_size(const char *name, unsigned size, + YYLTYPE loc, struct _mesa_glsl_parse_state *state) +{ + if ((strcmp("gl_TexCoord", name) == 0) + && (size > state->Const.MaxTextureCoords)) { + /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: + * + * "The size [of gl_TexCoord] can be at most + * gl_MaxTextureCoords." + */ + _mesa_glsl_error(&loc, state, "`gl_TexCoord' array size cannot " + "be larger than gl_MaxTextureCoords (%u)\n", + state->Const.MaxTextureCoords); + return true; + } else if (strcmp("gl_ClipDistance", name) == 0 + && size > state->Const.MaxClipPlanes) { + /* From section 7.1 (Vertex Shader Special Variables) of the + * GLSL 1.30 spec: + * + * "The gl_ClipDistance array is predeclared as unsized and + * must be sized by the shader either redeclaring it with a + * size or indexing it only with integral constant + * expressions. ... The size can be at most + * gl_MaxClipDistances." + */ + _mesa_glsl_error(&loc, state, "`gl_ClipDistance' array size cannot " + "be larger than gl_MaxClipDistances (%u)\n", + state->Const.MaxClipPlanes); + return true; + } + return false; +} + +ir_rvalue * +ast_expression::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + static const int operations[AST_NUM_OPERATORS] = { + -1, /* ast_assign doesn't convert to ir_expression. */ + -1, /* ast_plus doesn't convert to ir_expression. */ + ir_unop_neg, + ir_binop_add, + ir_binop_sub, + ir_binop_mul, + ir_binop_div, + ir_binop_mod, + ir_binop_lshift, + ir_binop_rshift, + ir_binop_less, + ir_binop_greater, + ir_binop_lequal, + ir_binop_gequal, + ir_binop_all_equal, + ir_binop_any_nequal, + ir_binop_bit_and, + ir_binop_bit_xor, + ir_binop_bit_or, + ir_unop_bit_not, + ir_binop_logic_and, + ir_binop_logic_xor, + ir_binop_logic_or, + ir_unop_logic_not, + + /* Note: The following block of expression types actually convert + * to multiple IR instructions. + */ + ir_binop_mul, /* ast_mul_assign */ + ir_binop_div, /* ast_div_assign */ + ir_binop_mod, /* ast_mod_assign */ + ir_binop_add, /* ast_add_assign */ + ir_binop_sub, /* ast_sub_assign */ + ir_binop_lshift, /* ast_ls_assign */ + ir_binop_rshift, /* ast_rs_assign */ + ir_binop_bit_and, /* ast_and_assign */ + ir_binop_bit_xor, /* ast_xor_assign */ + ir_binop_bit_or, /* ast_or_assign */ + + -1, /* ast_conditional doesn't convert to ir_expression. */ + ir_binop_add, /* ast_pre_inc. */ + ir_binop_sub, /* ast_pre_dec. */ + ir_binop_add, /* ast_post_inc. */ + ir_binop_sub, /* ast_post_dec. */ + -1, /* ast_field_selection doesn't conv to ir_expression. */ + -1, /* ast_array_index doesn't convert to ir_expression. */ + -1, /* ast_function_call doesn't conv to ir_expression. */ + -1, /* ast_identifier doesn't convert to ir_expression. */ + -1, /* ast_int_constant doesn't convert to ir_expression. */ + -1, /* ast_uint_constant doesn't conv to ir_expression. */ + -1, /* ast_float_constant doesn't conv to ir_expression. */ + -1, /* ast_bool_constant doesn't conv to ir_expression. */ + -1, /* ast_sequence doesn't convert to ir_expression. */ + }; + ir_rvalue *result = NULL; + ir_rvalue *op[3]; + const struct glsl_type *type; /* a temporary variable for switch cases */ + bool error_emitted = false; + YYLTYPE loc; + + loc = this->get_location(); + + switch (this->oper) { + case ast_assign: { + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + result = do_assignment(instructions, state, op[0], op[1], false, + this->subexpressions[0]->get_location()); + error_emitted = result->type->is_error(); + break; + } + + case ast_plus: + op[0] = this->subexpressions[0]->hir(instructions, state); + + type = unary_arithmetic_result_type(op[0]->type, state, & loc); + + error_emitted = type->is_error(); + + result = op[0]; + break; + + case ast_neg: + op[0] = this->subexpressions[0]->hir(instructions, state); + + type = unary_arithmetic_result_type(op[0]->type, state, & loc); + + error_emitted = type->is_error(); + + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], NULL); + break; + + case ast_add: + case ast_sub: + case ast_mul: + case ast_div: + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + type = arithmetic_result_type(op[0], op[1], + (this->oper == ast_mul), + state, & loc); + error_emitted = type->is_error(); + + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + break; + + case ast_mod: + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); + + assert(operations[this->oper] == ir_binop_mod); + + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + error_emitted = type->is_error(); + break; + + case ast_lshift: + case ast_rshift: + if (state->language_version < 130) { + _mesa_glsl_error(&loc, state, "operator %s requires GLSL 1.30", + operator_string(this->oper)); + error_emitted = true; + } + + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, + &loc); + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + break; + + case ast_less: + case ast_greater: + case ast_lequal: + case ast_gequal: + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + type = relational_result_type(op[0], op[1], state, & loc); + + /* The relational operators must either generate an error or result + * in a scalar boolean. See page 57 of the GLSL 1.50 spec. + */ + assert(type->is_error() + || ((type->base_type == GLSL_TYPE_BOOL) + && type->is_scalar())); + + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + error_emitted = type->is_error(); + break; + + case ast_nequal: + case ast_equal: + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + /* From page 58 (page 64 of the PDF) of the GLSL 1.50 spec: + * + * "The equality operators equal (==), and not equal (!=) + * operate on all types. They result in a scalar Boolean. If + * the operand types do not match, then there must be a + * conversion from Section 4.1.10 "Implicit Conversions" + * applied to one operand that can make them match, in which + * case this conversion is done." + */ + if ((!apply_implicit_conversion(op[0]->type, op[1], state) + && !apply_implicit_conversion(op[1]->type, op[0], state)) + || (op[0]->type != op[1]->type)) { + _mesa_glsl_error(& loc, state, "operands of `%s' must have the same " + "type", (this->oper == ast_equal) ? "==" : "!="); + error_emitted = true; + } else if ((state->language_version <= 110) + && (op[0]->type->is_array() || op[1]->type->is_array())) { + _mesa_glsl_error(& loc, state, "array comparisons forbidden in " + "GLSL 1.10"); + error_emitted = true; + } + + if (error_emitted) { + result = new(ctx) ir_constant(false); + } else { + result = do_comparison(ctx, operations[this->oper], op[0], op[1]); + assert(result->type == glsl_type::bool_type); + } + break; + + case ast_bit_and: + case ast_bit_xor: + case ast_bit_or: + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, + state, &loc); + result = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + break; + + case ast_bit_not: + op[0] = this->subexpressions[0]->hir(instructions, state); + + if (state->language_version < 130) { + _mesa_glsl_error(&loc, state, "bit-wise operations require GLSL 1.30"); + error_emitted = true; + } + + if (!op[0]->type->is_integer()) { + _mesa_glsl_error(&loc, state, "operand of `~' must be an integer"); + error_emitted = true; + } + + type = op[0]->type; + result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL); + break; + + case ast_logic_and: { + exec_list rhs_instructions; + op[0] = get_scalar_boolean_operand(instructions, state, this, 0, + "LHS", &error_emitted); + op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1, + "RHS", &error_emitted); + + ir_constant *op0_const = op[0]->constant_expression_value(); + if (op0_const) { + if (op0_const->value.b[0]) { + instructions->append_list(&rhs_instructions); + result = op[1]; + } else { + result = op0_const; + } + type = glsl_type::bool_type; + } else { + ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, + "and_tmp", + ir_var_temporary); + instructions->push_tail(tmp); + + ir_if *const stmt = new(ctx) ir_if(op[0]); + instructions->push_tail(stmt); + + stmt->then_instructions.append_list(&rhs_instructions); + ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); + ir_assignment *const then_assign = + new(ctx) ir_assignment(then_deref, op[1], NULL); + stmt->then_instructions.push_tail(then_assign); + + ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); + ir_assignment *const else_assign = + new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false), NULL); + stmt->else_instructions.push_tail(else_assign); + + result = new(ctx) ir_dereference_variable(tmp); + type = tmp->type; + } + break; + } + + case ast_logic_or: { + exec_list rhs_instructions; + op[0] = get_scalar_boolean_operand(instructions, state, this, 0, + "LHS", &error_emitted); + op[1] = get_scalar_boolean_operand(&rhs_instructions, state, this, 1, + "RHS", &error_emitted); + + ir_constant *op0_const = op[0]->constant_expression_value(); + if (op0_const) { + if (op0_const->value.b[0]) { + result = op0_const; + } else { + result = op[1]; + } + type = glsl_type::bool_type; + } else { + ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, + "or_tmp", + ir_var_temporary); + instructions->push_tail(tmp); + + ir_if *const stmt = new(ctx) ir_if(op[0]); + instructions->push_tail(stmt); + + ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); + ir_assignment *const then_assign = + new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true), NULL); + stmt->then_instructions.push_tail(then_assign); + + stmt->else_instructions.append_list(&rhs_instructions); + ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); + ir_assignment *const else_assign = + new(ctx) ir_assignment(else_deref, op[1], NULL); + stmt->else_instructions.push_tail(else_assign); + + result = new(ctx) ir_dereference_variable(tmp); + type = tmp->type; + } + break; + } + + case ast_logic_xor: + /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec: + * + * "The logical binary operators and (&&), or ( | | ), and + * exclusive or (^^). They operate only on two Boolean + * expressions and result in a Boolean expression." + */ + op[0] = get_scalar_boolean_operand(instructions, state, this, 0, "LHS", + &error_emitted); + op[1] = get_scalar_boolean_operand(instructions, state, this, 1, "RHS", + &error_emitted); + + result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, + op[0], op[1]); + break; + + case ast_logic_not: + op[0] = get_scalar_boolean_operand(instructions, state, this, 0, + "operand", &error_emitted); + + result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, + op[0], NULL); + break; + + case ast_mul_assign: + case ast_div_assign: + case ast_add_assign: + case ast_sub_assign: { + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + type = arithmetic_result_type(op[0], op[1], + (this->oper == ast_mul_assign), + state, & loc); + + ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + + result = do_assignment(instructions, state, + op[0]->clone(ctx, NULL), temp_rhs, false, + this->subexpressions[0]->get_location()); + error_emitted = (op[0]->type->is_error()); + + /* GLSL 1.10 does not allow array assignment. However, we don't have to + * explicitly test for this because none of the binary expression + * operators allow array operands either. + */ + + break; + } + + case ast_mod_assign: { + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + + type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); + + assert(operations[this->oper] == ir_binop_mod); + + ir_rvalue *temp_rhs; + temp_rhs = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + + result = do_assignment(instructions, state, + op[0]->clone(ctx, NULL), temp_rhs, false, + this->subexpressions[0]->get_location()); + error_emitted = type->is_error(); + break; + } + + case ast_ls_assign: + case ast_rs_assign: { + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, + &loc); + ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], + type, op[0], op[1]); + result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), + temp_rhs, false, + this->subexpressions[0]->get_location()); + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + break; + } + + case ast_and_assign: + case ast_xor_assign: + case ast_or_assign: { + op[0] = this->subexpressions[0]->hir(instructions, state); + op[1] = this->subexpressions[1]->hir(instructions, state); + type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, + state, &loc); + ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], + type, op[0], op[1]); + result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), + temp_rhs, false, + this->subexpressions[0]->get_location()); + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + break; + } + + case ast_conditional: { + /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: + * + * "The ternary selection operator (?:). It operates on three + * expressions (exp1 ? exp2 : exp3). This operator evaluates the + * first expression, which must result in a scalar Boolean." + */ + op[0] = get_scalar_boolean_operand(instructions, state, this, 0, + "condition", &error_emitted); + + /* The :? operator is implemented by generating an anonymous temporary + * followed by an if-statement. The last instruction in each branch of + * the if-statement assigns a value to the anonymous temporary. This + * temporary is the r-value of the expression. + */ + exec_list then_instructions; + exec_list else_instructions; + + op[1] = this->subexpressions[1]->hir(&then_instructions, state); + op[2] = this->subexpressions[2]->hir(&else_instructions, state); + + /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: + * + * "The second and third expressions can be any type, as + * long their types match, or there is a conversion in + * Section 4.1.10 "Implicit Conversions" that can be applied + * to one of the expressions to make their types match. This + * resulting matching type is the type of the entire + * expression." + */ + if ((!apply_implicit_conversion(op[1]->type, op[2], state) + && !apply_implicit_conversion(op[2]->type, op[1], state)) + || (op[1]->type != op[2]->type)) { + YYLTYPE loc = this->subexpressions[1]->get_location(); + + _mesa_glsl_error(& loc, state, "Second and third operands of ?: " + "operator must have matching types."); + error_emitted = true; + type = glsl_type::error_type; + } else { + type = op[1]->type; + } + + /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec: + * + * "The second and third expressions must be the same type, but can + * be of any type other than an array." + */ + if ((state->language_version <= 110) && type->is_array()) { + _mesa_glsl_error(& loc, state, "Second and third operands of ?: " + "operator must not be arrays."); + error_emitted = true; + } + + ir_constant *cond_val = op[0]->constant_expression_value(); + ir_constant *then_val = op[1]->constant_expression_value(); + ir_constant *else_val = op[2]->constant_expression_value(); + + if (then_instructions.is_empty() + && else_instructions.is_empty() + && (cond_val != NULL) && (then_val != NULL) && (else_val != NULL)) { + result = (cond_val->value.b[0]) ? then_val : else_val; + } else { + ir_variable *const tmp = + new(ctx) ir_variable(type, "conditional_tmp", ir_var_temporary); + instructions->push_tail(tmp); + + ir_if *const stmt = new(ctx) ir_if(op[0]); + instructions->push_tail(stmt); + + then_instructions.move_nodes_to(& stmt->then_instructions); + ir_dereference *const then_deref = + new(ctx) ir_dereference_variable(tmp); + ir_assignment *const then_assign = + new(ctx) ir_assignment(then_deref, op[1], NULL); + stmt->then_instructions.push_tail(then_assign); + + else_instructions.move_nodes_to(& stmt->else_instructions); + ir_dereference *const else_deref = + new(ctx) ir_dereference_variable(tmp); + ir_assignment *const else_assign = + new(ctx) ir_assignment(else_deref, op[2], NULL); + stmt->else_instructions.push_tail(else_assign); + + result = new(ctx) ir_dereference_variable(tmp); + } + break; + } + + case ast_pre_inc: + case ast_pre_dec: { + op[0] = this->subexpressions[0]->hir(instructions, state); + if (op[0]->type->base_type == GLSL_TYPE_FLOAT) + op[1] = new(ctx) ir_constant(1.0f); + else + op[1] = new(ctx) ir_constant(1); + + type = arithmetic_result_type(op[0], op[1], false, state, & loc); + + ir_rvalue *temp_rhs; + temp_rhs = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + + result = do_assignment(instructions, state, + op[0]->clone(ctx, NULL), temp_rhs, false, + this->subexpressions[0]->get_location()); + error_emitted = op[0]->type->is_error(); + break; + } + + case ast_post_inc: + case ast_post_dec: { + op[0] = this->subexpressions[0]->hir(instructions, state); + if (op[0]->type->base_type == GLSL_TYPE_FLOAT) + op[1] = new(ctx) ir_constant(1.0f); + else + op[1] = new(ctx) ir_constant(1); + + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + + type = arithmetic_result_type(op[0], op[1], false, state, & loc); + + ir_rvalue *temp_rhs; + temp_rhs = new(ctx) ir_expression(operations[this->oper], type, + op[0], op[1]); + + /* Get a temporary of a copy of the lvalue before it's modified. + * This may get thrown away later. + */ + result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL)); + + (void)do_assignment(instructions, state, + op[0]->clone(ctx, NULL), temp_rhs, false, + this->subexpressions[0]->get_location()); + + error_emitted = op[0]->type->is_error(); + break; + } + + case ast_field_selection: + result = _mesa_ast_field_selection_to_hir(this, instructions, state); + break; + + case ast_array_index: { + YYLTYPE index_loc = subexpressions[1]->get_location(); + + op[0] = subexpressions[0]->hir(instructions, state); + op[1] = subexpressions[1]->hir(instructions, state); + + error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); + + ir_rvalue *const array = op[0]; + + result = new(ctx) ir_dereference_array(op[0], op[1]); + + /* Do not use op[0] after this point. Use array. + */ + op[0] = NULL; + + + if (error_emitted) + break; + + if (!array->type->is_array() + && !array->type->is_matrix() + && !array->type->is_vector()) { + _mesa_glsl_error(& index_loc, state, + "cannot dereference non-array / non-matrix / " + "non-vector"); + error_emitted = true; + } + + if (!op[1]->type->is_integer()) { + _mesa_glsl_error(& index_loc, state, + "array index must be integer type"); + error_emitted = true; + } else if (!op[1]->type->is_scalar()) { + _mesa_glsl_error(& index_loc, state, + "array index must be scalar"); + error_emitted = true; + } + + /* If the array index is a constant expression and the array has a + * declared size, ensure that the access is in-bounds. If the array + * index is not a constant expression, ensure that the array has a + * declared size. + */ + ir_constant *const const_index = op[1]->constant_expression_value(); + if (const_index != NULL) { + const int idx = const_index->value.i[0]; + const char *type_name; + unsigned bound = 0; + + if (array->type->is_matrix()) { + type_name = "matrix"; + } else if (array->type->is_vector()) { + type_name = "vector"; + } else { + type_name = "array"; + } + + /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec: + * + * "It is illegal to declare an array with a size, and then + * later (in the same shader) index the same array with an + * integral constant expression greater than or equal to the + * declared size. It is also illegal to index an array with a + * negative constant expression." + */ + if (array->type->is_matrix()) { + if (array->type->row_type()->vector_elements <= idx) { + bound = array->type->row_type()->vector_elements; + } + } else if (array->type->is_vector()) { + if (array->type->vector_elements <= idx) { + bound = array->type->vector_elements; + } + } else { + if ((array->type->array_size() > 0) + && (array->type->array_size() <= idx)) { + bound = array->type->array_size(); + } + } + + if (bound > 0) { + _mesa_glsl_error(& loc, state, "%s index must be < %u", + type_name, bound); + error_emitted = true; + } else if (idx < 0) { + _mesa_glsl_error(& loc, state, "%s index must be >= 0", + type_name); + error_emitted = true; + } + + if (array->type->is_array()) { + /* If the array is a variable dereference, it dereferences the + * whole array, by definition. Use this to get the variable. + * + * FINISHME: Should some methods for getting / setting / testing + * FINISHME: array access limits be added to ir_dereference? + */ + ir_variable *const v = array->whole_variable_referenced(); + if ((v != NULL) && (unsigned(idx) > v->max_array_access)) { + v->max_array_access = idx; + + /* Check whether this access will, as a side effect, implicitly + * cause the size of a built-in array to be too large. + */ + if (check_builtin_array_max_size(v->name, idx+1, loc, state)) + error_emitted = true; + } + } + } else if (array->type->array_size() == 0) { + _mesa_glsl_error(&loc, state, "unsized array index must be constant"); + } else { + if (array->type->is_array()) { + /* whole_variable_referenced can return NULL if the array is a + * member of a structure. In this case it is safe to not update + * the max_array_access field because it is never used for fields + * of structures. + */ + ir_variable *v = array->whole_variable_referenced(); + if (v != NULL) + v->max_array_access = array->type->array_size() - 1; + } + } + + /* From page 23 (29 of the PDF) of the GLSL 1.30 spec: + * + * "Samplers aggregated into arrays within a shader (using square + * brackets [ ]) can only be indexed with integral constant + * expressions [...]." + * + * This restriction was added in GLSL 1.30. Shaders using earlier version + * of the language should not be rejected by the compiler front-end for + * using this construct. This allows useful things such as using a loop + * counter as the index to an array of samplers. If the loop in unrolled, + * the code should compile correctly. Instead, emit a warning. + */ + if (array->type->is_array() && + array->type->element_type()->is_sampler() && + const_index == NULL) { + + if (state->language_version == 100) { + _mesa_glsl_warning(&loc, state, + "sampler arrays indexed with non-constant " + "expressions is optional in GLSL ES 1.00"); + } else if (state->language_version < 130) { + _mesa_glsl_warning(&loc, state, + "sampler arrays indexed with non-constant " + "expressions is forbidden in GLSL 1.30 and " + "later"); + } else { + _mesa_glsl_error(&loc, state, + "sampler arrays indexed with non-constant " + "expressions is forbidden in GLSL 1.30 and " + "later"); + error_emitted = true; + } + } + + if (error_emitted) + result->type = glsl_type::error_type; + + break; + } + + case ast_function_call: + /* Should *NEVER* get here. ast_function_call should always be handled + * by ast_function_expression::hir. + */ + assert(0); + break; + + case ast_identifier: { + /* ast_identifier can appear several places in a full abstract syntax + * tree. This particular use must be at location specified in the grammar + * as 'variable_identifier'. + */ + ir_variable *var = + state->symbols->get_variable(this->primary_expression.identifier); + + result = new(ctx) ir_dereference_variable(var); + + if (var != NULL) { + var->used = true; + } else { + _mesa_glsl_error(& loc, state, "`%s' undeclared", + this->primary_expression.identifier); + + error_emitted = true; + } + break; + } + + case ast_int_constant: + result = new(ctx) ir_constant(this->primary_expression.int_constant); + break; + + case ast_uint_constant: + result = new(ctx) ir_constant(this->primary_expression.uint_constant); + break; + + case ast_float_constant: + result = new(ctx) ir_constant(this->primary_expression.float_constant); + break; + + case ast_bool_constant: + result = new(ctx) ir_constant(bool(this->primary_expression.bool_constant)); + break; + + case ast_sequence: { + /* It should not be possible to generate a sequence in the AST without + * any expressions in it. + */ + assert(!this->expressions.is_empty()); + + /* The r-value of a sequence is the last expression in the sequence. If + * the other expressions in the sequence do not have side-effects (and + * therefore add instructions to the instruction list), they get dropped + * on the floor. + */ + exec_node *previous_tail_pred = NULL; + YYLTYPE previous_operand_loc = loc; + + foreach_list_typed (ast_node, ast, link, &this->expressions) { + /* If one of the operands of comma operator does not generate any + * code, we want to emit a warning. At each pass through the loop + * previous_tail_pred will point to the last instruction in the + * stream *before* processing the previous operand. Naturally, + * instructions->tail_pred will point to the last instruction in the + * stream *after* processing the previous operand. If the two + * pointers match, then the previous operand had no effect. + * + * The warning behavior here differs slightly from GCC. GCC will + * only emit a warning if none of the left-hand operands have an + * effect. However, it will emit a warning for each. I believe that + * there are some cases in C (especially with GCC extensions) where + * it is useful to have an intermediate step in a sequence have no + * effect, but I don't think these cases exist in GLSL. Either way, + * it would be a giant hassle to replicate that behavior. + */ + if (previous_tail_pred == instructions->tail_pred) { + _mesa_glsl_warning(&previous_operand_loc, state, + "left-hand operand of comma expression has " + "no effect"); + } + + /* tail_pred is directly accessed instead of using the get_tail() + * method for performance reasons. get_tail() has extra code to + * return NULL when the list is empty. We don't care about that + * here, so using tail_pred directly is fine. + */ + previous_tail_pred = instructions->tail_pred; + previous_operand_loc = ast->get_location(); + + result = ast->hir(instructions, state); + } + + /* Any errors should have already been emitted in the loop above. + */ + error_emitted = true; + break; + } + } + type = NULL; /* use result->type, not type. */ + assert(result != NULL); + + if (result->type->is_error() && !error_emitted) + _mesa_glsl_error(& loc, state, "type mismatch"); + + return result; +} + + +ir_rvalue * +ast_expression_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + /* It is possible to have expression statements that don't have an + * expression. This is the solitary semicolon: + * + * for (i = 0; i < 5; i++) + * ; + * + * In this case the expression will be NULL. Test for NULL and don't do + * anything in that case. + */ + if (expression != NULL) + expression->hir(instructions, state); + + /* Statements do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_compound_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + if (new_scope) + state->symbols->push_scope(); + + foreach_list_typed (ast_node, ast, link, &this->statements) + ast->hir(instructions, state); + + if (new_scope) + state->symbols->pop_scope(); + + /* Compound statements do not have r-values. + */ + return NULL; +} + + +static const glsl_type * +process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size, + struct _mesa_glsl_parse_state *state) +{ + unsigned length = 0; + + /* FINISHME: Reject delcarations of multidimensional arrays. */ + + if (array_size != NULL) { + exec_list dummy_instructions; + ir_rvalue *const ir = array_size->hir(& dummy_instructions, state); + YYLTYPE loc = array_size->get_location(); + + if (ir != NULL) { + if (!ir->type->is_integer()) { + _mesa_glsl_error(& loc, state, "array size must be integer type"); + } else if (!ir->type->is_scalar()) { + _mesa_glsl_error(& loc, state, "array size must be scalar type"); + } else { + ir_constant *const size = ir->constant_expression_value(); + + if (size == NULL) { + _mesa_glsl_error(& loc, state, "array size must be a " + "constant valued expression"); + } else if (size->value.i[0] <= 0) { + _mesa_glsl_error(& loc, state, "array size must be > 0"); + } else { + assert(size->type == ir->type); + length = size->value.u[0]; + + /* If the array size is const (and we've verified that + * it is) then no instructions should have been emitted + * when we converted it to HIR. If they were emitted, + * then either the array size isn't const after all, or + * we are emitting unnecessary instructions. + */ + assert(dummy_instructions.is_empty()); + } + } + } + } else if (state->es_shader) { + /* Section 10.17 of the GLSL ES 1.00 specification states that unsized + * array declarations have been removed from the language. + */ + _mesa_glsl_error(loc, state, "unsized array declarations are not " + "allowed in GLSL ES 1.00."); + } + + return glsl_type::get_array_instance(base, length); +} + + +const glsl_type * +ast_type_specifier::glsl_type(const char **name, + struct _mesa_glsl_parse_state *state) const +{ + const struct glsl_type *type; + + type = state->symbols->get_type(this->type_name); + *name = this->type_name; + + if (this->is_array) { + YYLTYPE loc = this->get_location(); + type = process_array_type(&loc, type, this->array_size, state); + } + + return type; +} + + +static void +apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual, + ir_variable *var, + struct _mesa_glsl_parse_state *state, + YYLTYPE *loc) +{ + if (qual->flags.q.invariant) { + if (var->used) { + _mesa_glsl_error(loc, state, + "variable `%s' may not be redeclared " + "`invariant' after being used", + var->name); + } else { + var->invariant = 1; + } + } + + if (qual->flags.q.constant || qual->flags.q.attribute + || qual->flags.q.uniform + || (qual->flags.q.varying && (state->target == fragment_shader))) + var->read_only = 1; + + if (qual->flags.q.centroid) + var->centroid = 1; + + if (qual->flags.q.attribute && state->target != vertex_shader) { + var->type = glsl_type::error_type; + _mesa_glsl_error(loc, state, + "`attribute' variables may not be declared in the " + "%s shader", + _mesa_glsl_shader_target_name(state->target)); + } + + /* From page 25 (page 31 of the PDF) of the GLSL 1.10 spec: + * + * "The varying qualifier can be used only with the data types + * float, vec2, vec3, vec4, mat2, mat3, and mat4, or arrays of + * these." + */ + if (qual->flags.q.varying) { + const glsl_type *non_array_type; + + if (var->type && var->type->is_array()) + non_array_type = var->type->fields.array; + else + non_array_type = var->type; + + if (non_array_type && non_array_type->base_type != GLSL_TYPE_FLOAT) { + var->type = glsl_type::error_type; + _mesa_glsl_error(loc, state, + "varying variables must be of base type float"); + } + } + + /* If there is no qualifier that changes the mode of the variable, leave + * the setting alone. + */ + if (qual->flags.q.in && qual->flags.q.out) + var->mode = ir_var_inout; + else if (qual->flags.q.attribute || qual->flags.q.in + || (qual->flags.q.varying && (state->target == fragment_shader))) + var->mode = ir_var_in; + else if (qual->flags.q.out + || (qual->flags.q.varying && (state->target == vertex_shader))) + var->mode = ir_var_out; + else if (qual->flags.q.uniform) + var->mode = ir_var_uniform; + + if (state->all_invariant && (state->current_function == NULL)) { + switch (state->target) { + case vertex_shader: + if (var->mode == ir_var_out) + var->invariant = true; + break; + case geometry_shader: + if ((var->mode == ir_var_in) || (var->mode == ir_var_out)) + var->invariant = true; + break; + case fragment_shader: + if (var->mode == ir_var_in) + var->invariant = true; + break; + } + } + + if (qual->flags.q.flat) + var->interpolation = ir_var_flat; + else if (qual->flags.q.noperspective) + var->interpolation = ir_var_noperspective; + else + var->interpolation = ir_var_smooth; + + var->pixel_center_integer = qual->flags.q.pixel_center_integer; + var->origin_upper_left = qual->flags.q.origin_upper_left; + if ((qual->flags.q.origin_upper_left || qual->flags.q.pixel_center_integer) + && (strcmp(var->name, "gl_FragCoord") != 0)) { + const char *const qual_string = (qual->flags.q.origin_upper_left) + ? "origin_upper_left" : "pixel_center_integer"; + + _mesa_glsl_error(loc, state, + "layout qualifier `%s' can only be applied to " + "fragment shader input `gl_FragCoord'", + qual_string); + } + + if (qual->flags.q.explicit_location) { + const bool global_scope = (state->current_function == NULL); + bool fail = false; + const char *string = ""; + + /* In the vertex shader only shader inputs can be given explicit + * locations. + * + * In the fragment shader only shader outputs can be given explicit + * locations. + */ + switch (state->target) { + case vertex_shader: + if (!global_scope || (var->mode != ir_var_in)) { + fail = true; + string = "input"; + } + break; + + case geometry_shader: + _mesa_glsl_error(loc, state, + "geometry shader variables cannot be given " + "explicit locations\n"); + break; + + case fragment_shader: + if (!global_scope || (var->mode != ir_var_out)) { + fail = true; + string = "output"; + } + break; + }; + + if (fail) { + _mesa_glsl_error(loc, state, + "only %s shader %s variables can be given an " + "explicit location\n", + _mesa_glsl_shader_target_name(state->target), + string); + } else { + var->explicit_location = true; + + /* This bit of silliness is needed because invalid explicit locations + * are supposed to be flagged during linking. Small negative values + * biased by VERT_ATTRIB_GENERIC0 or FRAG_RESULT_DATA0 could alias + * built-in values (e.g., -16+VERT_ATTRIB_GENERIC0 = VERT_ATTRIB_POS). + * The linker needs to be able to differentiate these cases. This + * ensures that negative values stay negative. + */ + if (qual->location >= 0) { + var->location = (state->target == vertex_shader) + ? (qual->location + VERT_ATTRIB_GENERIC0) + : (qual->location + FRAG_RESULT_DATA0); + } else { + var->location = qual->location; + } + } + } + + /* Does the declaration use the 'layout' keyword? + */ + const bool uses_layout = qual->flags.q.pixel_center_integer + || qual->flags.q.origin_upper_left + || qual->flags.q.explicit_location; + + /* Does the declaration use the deprecated 'attribute' or 'varying' + * keywords? + */ + const bool uses_deprecated_qualifier = qual->flags.q.attribute + || qual->flags.q.varying; + + /* Is the 'layout' keyword used with parameters that allow relaxed checking. + * Many implementations of GL_ARB_fragment_coord_conventions_enable and some + * implementations (only Mesa?) GL_ARB_explicit_attrib_location_enable + * allowed the layout qualifier to be used with 'varying' and 'attribute'. + * These extensions and all following extensions that add the 'layout' + * keyword have been modified to require the use of 'in' or 'out'. + * + * The following extension do not allow the deprecated keywords: + * + * GL_AMD_conservative_depth + * GL_ARB_gpu_shader5 + * GL_ARB_separate_shader_objects + * GL_ARB_tesselation_shader + * GL_ARB_transform_feedback3 + * GL_ARB_uniform_buffer_object + * + * It is unknown whether GL_EXT_shader_image_load_store or GL_NV_gpu_shader5 + * allow layout with the deprecated keywords. + */ + const bool relaxed_layout_qualifier_checking = + state->ARB_fragment_coord_conventions_enable; + + if (uses_layout && uses_deprecated_qualifier) { + if (relaxed_layout_qualifier_checking) { + _mesa_glsl_warning(loc, state, + "`layout' qualifier may not be used with " + "`attribute' or `varying'"); + } else { + _mesa_glsl_error(loc, state, + "`layout' qualifier may not be used with " + "`attribute' or `varying'"); + } + } + + /* Layout qualifiers for gl_FragDepth, which are enabled by extension + * AMD_conservative_depth. + */ + int depth_layout_count = qual->flags.q.depth_any + + qual->flags.q.depth_greater + + qual->flags.q.depth_less + + qual->flags.q.depth_unchanged; + if (depth_layout_count > 0 + && !state->AMD_conservative_depth_enable) { + _mesa_glsl_error(loc, state, + "extension GL_AMD_conservative_depth must be enabled " + "to use depth layout qualifiers"); + } else if (depth_layout_count > 0 + && strcmp(var->name, "gl_FragDepth") != 0) { + _mesa_glsl_error(loc, state, + "depth layout qualifiers can be applied only to " + "gl_FragDepth"); + } else if (depth_layout_count > 1 + && strcmp(var->name, "gl_FragDepth") == 0) { + _mesa_glsl_error(loc, state, + "at most one depth layout qualifier can be applied to " + "gl_FragDepth"); + } + if (qual->flags.q.depth_any) + var->depth_layout = ir_depth_layout_any; + else if (qual->flags.q.depth_greater) + var->depth_layout = ir_depth_layout_greater; + else if (qual->flags.q.depth_less) + var->depth_layout = ir_depth_layout_less; + else if (qual->flags.q.depth_unchanged) + var->depth_layout = ir_depth_layout_unchanged; + else + var->depth_layout = ir_depth_layout_none; + + /* From page 46 (page 52 of the PDF) of the GLSL ES specification: + * + * "Array variables are l-values and may be passed to parameters + * declared as out or inout. However, they may not be used as + * the target of an assignment." + * + * From page 32 (page 38 of the PDF) of the GLSL 1.10 spec: + * + * "Other binary or unary expressions, non-dereferenced arrays, + * function names, swizzles with repeated fields, and constants + * cannot be l-values." + * + * So we only mark 1.10 as non-lvalues, and check for array + * assignment in 100 specifically in do_assignment. + */ + if (var->type->is_array() && state->language_version != 110) { + var->array_lvalue = true; + } +} + +/** + * Get the variable that is being redeclared by this declaration + * + * Semantic checks to verify the validity of the redeclaration are also + * performed. If semantic checks fail, compilation error will be emitted via + * \c _mesa_glsl_error, but a non-\c NULL pointer will still be returned. + * + * \returns + * A pointer to an existing variable in the current scope if the declaration + * is a redeclaration, \c NULL otherwise. + */ +ir_variable * +get_variable_being_redeclared(ir_variable *var, ast_declaration *decl, + struct _mesa_glsl_parse_state *state) +{ + /* Check if this declaration is actually a re-declaration, either to + * resize an array or add qualifiers to an existing variable. + * + * This is allowed for variables in the current scope, or when at + * global scope (for built-ins in the implicit outer scope). + */ + ir_variable *earlier = state->symbols->get_variable(decl->identifier); + if (earlier == NULL || + (state->current_function != NULL && + !state->symbols->name_declared_this_scope(decl->identifier))) { + return NULL; + } + + + YYLTYPE loc = decl->get_location(); + + /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec, + * + * "It is legal to declare an array without a size and then + * later re-declare the same name as an array of the same + * type and specify a size." + */ + if ((earlier->type->array_size() == 0) + && var->type->is_array() + && (var->type->element_type() == earlier->type->element_type())) { + /* FINISHME: This doesn't match the qualifiers on the two + * FINISHME: declarations. It's not 100% clear whether this is + * FINISHME: required or not. + */ + + const unsigned size = unsigned(var->type->array_size()); + check_builtin_array_max_size(var->name, size, loc, state); + if ((size > 0) && (size <= earlier->max_array_access)) { + _mesa_glsl_error(& loc, state, "array size must be > %u due to " + "previous access", + earlier->max_array_access); + } + + earlier->type = var->type; + delete var; + var = NULL; + } else if (state->ARB_fragment_coord_conventions_enable + && strcmp(var->name, "gl_FragCoord") == 0 + && earlier->type == var->type + && earlier->mode == var->mode) { + /* Allow redeclaration of gl_FragCoord for ARB_fcc layout + * qualifiers. + */ + earlier->origin_upper_left = var->origin_upper_left; + earlier->pixel_center_integer = var->pixel_center_integer; + + /* According to section 4.3.7 of the GLSL 1.30 spec, + * the following built-in varaibles can be redeclared with an + * interpolation qualifier: + * * gl_FrontColor + * * gl_BackColor + * * gl_FrontSecondaryColor + * * gl_BackSecondaryColor + * * gl_Color + * * gl_SecondaryColor + */ + } else if (state->language_version >= 130 + && (strcmp(var->name, "gl_FrontColor") == 0 + || strcmp(var->name, "gl_BackColor") == 0 + || strcmp(var->name, "gl_FrontSecondaryColor") == 0 + || strcmp(var->name, "gl_BackSecondaryColor") == 0 + || strcmp(var->name, "gl_Color") == 0 + || strcmp(var->name, "gl_SecondaryColor") == 0) + && earlier->type == var->type + && earlier->mode == var->mode) { + earlier->interpolation = var->interpolation; + + /* Layout qualifiers for gl_FragDepth. */ + } else if (state->AMD_conservative_depth_enable + && strcmp(var->name, "gl_FragDepth") == 0 + && earlier->type == var->type + && earlier->mode == var->mode) { + + /** From the AMD_conservative_depth spec: + * Within any shader, the first redeclarations of gl_FragDepth + * must appear before any use of gl_FragDepth. + */ + if (earlier->used) { + _mesa_glsl_error(&loc, state, + "the first redeclaration of gl_FragDepth " + "must appear before any use of gl_FragDepth"); + } + + /* Prevent inconsistent redeclaration of depth layout qualifier. */ + if (earlier->depth_layout != ir_depth_layout_none + && earlier->depth_layout != var->depth_layout) { + _mesa_glsl_error(&loc, state, + "gl_FragDepth: depth layout is declared here " + "as '%s, but it was previously declared as " + "'%s'", + depth_layout_string(var->depth_layout), + depth_layout_string(earlier->depth_layout)); + } + + earlier->depth_layout = var->depth_layout; + + } else { + _mesa_glsl_error(&loc, state, "`%s' redeclared", decl->identifier); + } + + return earlier; +} + +/** + * Generate the IR for an initializer in a variable declaration + */ +ir_rvalue * +process_initializer(ir_variable *var, ast_declaration *decl, + ast_fully_specified_type *type, + exec_list *initializer_instructions, + struct _mesa_glsl_parse_state *state) +{ + ir_rvalue *result = NULL; + + YYLTYPE initializer_loc = decl->initializer->get_location(); + + /* From page 24 (page 30 of the PDF) of the GLSL 1.10 spec: + * + * "All uniform variables are read-only and are initialized either + * directly by an application via API commands, or indirectly by + * OpenGL." + */ + if ((state->language_version <= 110) + && (var->mode == ir_var_uniform)) { + _mesa_glsl_error(& initializer_loc, state, + "cannot initialize uniforms in GLSL 1.10"); + } + + if (var->type->is_sampler()) { + _mesa_glsl_error(& initializer_loc, state, + "cannot initialize samplers"); + } + + if ((var->mode == ir_var_in) && (state->current_function == NULL)) { + _mesa_glsl_error(& initializer_loc, state, + "cannot initialize %s shader input / %s", + _mesa_glsl_shader_target_name(state->target), + (state->target == vertex_shader) + ? "attribute" : "varying"); + } + + ir_dereference *const lhs = new(state) ir_dereference_variable(var); + ir_rvalue *rhs = decl->initializer->hir(initializer_instructions, + state); + + /* Calculate the constant value if this is a const or uniform + * declaration. + */ + if (type->qualifier.flags.q.constant + || type->qualifier.flags.q.uniform) { + ir_rvalue *new_rhs = validate_assignment(state, var->type, rhs, true); + if (new_rhs != NULL) { + rhs = new_rhs; + + ir_constant *constant_value = rhs->constant_expression_value(); + if (!constant_value) { + _mesa_glsl_error(& initializer_loc, state, + "initializer of %s variable `%s' must be a " + "constant expression", + (type->qualifier.flags.q.constant) + ? "const" : "uniform", + decl->identifier); + if (var->type->is_numeric()) { + /* Reduce cascading errors. */ + var->constant_value = ir_constant::zero(state, var->type); + } + } else { + rhs = constant_value; + var->constant_value = constant_value; + } + } else { + _mesa_glsl_error(&initializer_loc, state, + "initializer of type %s cannot be assigned to " + "variable of type %s", + rhs->type->name, var->type->name); + if (var->type->is_numeric()) { + /* Reduce cascading errors. */ + var->constant_value = ir_constant::zero(state, var->type); + } + } + } + + if (rhs && !rhs->type->is_error()) { + bool temp = var->read_only; + if (type->qualifier.flags.q.constant) + var->read_only = false; + + /* Never emit code to initialize a uniform. + */ + const glsl_type *initializer_type; + if (!type->qualifier.flags.q.uniform) { + result = do_assignment(initializer_instructions, state, + lhs, rhs, true, + type->get_location()); + initializer_type = result->type; + } else + initializer_type = rhs->type; + + /* If the declared variable is an unsized array, it must inherrit + * its full type from the initializer. A declaration such as + * + * uniform float a[] = float[](1.0, 2.0, 3.0, 3.0); + * + * becomes + * + * uniform float a[4] = float[](1.0, 2.0, 3.0, 3.0); + * + * The assignment generated in the if-statement (below) will also + * automatically handle this case for non-uniforms. + * + * If the declared variable is not an array, the types must + * already match exactly. As a result, the type assignment + * here can be done unconditionally. For non-uniforms the call + * to do_assignment can change the type of the initializer (via + * the implicit conversion rules). For uniforms the initializer + * must be a constant expression, and the type of that expression + * was validated above. + */ + var->type = initializer_type; + + var->read_only = temp; + } + + return result; +} + +ir_rvalue * +ast_declarator_list::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + const struct glsl_type *decl_type; + const char *type_name = NULL; + ir_rvalue *result = NULL; + YYLTYPE loc = this->get_location(); + + /* From page 46 (page 52 of the PDF) of the GLSL 1.50 spec: + * + * "To ensure that a particular output variable is invariant, it is + * necessary to use the invariant qualifier. It can either be used to + * qualify a previously declared variable as being invariant + * + * invariant gl_Position; // make existing gl_Position be invariant" + * + * In these cases the parser will set the 'invariant' flag in the declarator + * list, and the type will be NULL. + */ + if (this->invariant) { + assert(this->type == NULL); + + if (state->current_function != NULL) { + _mesa_glsl_error(& loc, state, + "All uses of `invariant' keyword must be at global " + "scope\n"); + } + + foreach_list_typed (ast_declaration, decl, link, &this->declarations) { + assert(!decl->is_array); + assert(decl->array_size == NULL); + assert(decl->initializer == NULL); + + ir_variable *const earlier = + state->symbols->get_variable(decl->identifier); + if (earlier == NULL) { + _mesa_glsl_error(& loc, state, + "Undeclared variable `%s' cannot be marked " + "invariant\n", decl->identifier); + } else if ((state->target == vertex_shader) + && (earlier->mode != ir_var_out)) { + _mesa_glsl_error(& loc, state, + "`%s' cannot be marked invariant, vertex shader " + "outputs only\n", decl->identifier); + } else if ((state->target == fragment_shader) + && (earlier->mode != ir_var_in)) { + _mesa_glsl_error(& loc, state, + "`%s' cannot be marked invariant, fragment shader " + "inputs only\n", decl->identifier); + } else if (earlier->used) { + _mesa_glsl_error(& loc, state, + "variable `%s' may not be redeclared " + "`invariant' after being used", + earlier->name); + } else { + earlier->invariant = true; + } + } + + /* Invariant redeclarations do not have r-values. + */ + return NULL; + } + + assert(this->type != NULL); + assert(!this->invariant); + + /* The type specifier may contain a structure definition. Process that + * before any of the variable declarations. + */ + (void) this->type->specifier->hir(instructions, state); + + decl_type = this->type->specifier->glsl_type(& type_name, state); + if (this->declarations.is_empty()) { + if (decl_type != NULL) { + /* Warn if this empty declaration is not for declaring a structure. + */ + if (this->type->specifier->structure == NULL) { + _mesa_glsl_warning(&loc, state, "empty declaration"); + } + } else { + _mesa_glsl_error(& loc, state, "incomplete declaration"); + } + } + + foreach_list_typed (ast_declaration, decl, link, &this->declarations) { + const struct glsl_type *var_type; + ir_variable *var; + + /* FINISHME: Emit a warning if a variable declaration shadows a + * FINISHME: declaration at a higher scope. + */ + + if ((decl_type == NULL) || decl_type->is_void()) { + if (type_name != NULL) { + _mesa_glsl_error(& loc, state, + "invalid type `%s' in declaration of `%s'", + type_name, decl->identifier); + } else { + _mesa_glsl_error(& loc, state, + "invalid type in declaration of `%s'", + decl->identifier); + } + continue; + } + + if (decl->is_array) { + var_type = process_array_type(&loc, decl_type, decl->array_size, + state); + } else { + var_type = decl_type; + } + + var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto); + + /* From page 22 (page 28 of the PDF) of the GLSL 1.10 specification; + * + * "Global variables can only use the qualifiers const, + * attribute, uni form, or varying. Only one may be + * specified. + * + * Local variables can only use the qualifier const." + * + * This is relaxed in GLSL 1.30. It is also relaxed by any extension + * that adds the 'layout' keyword. + */ + if ((state->language_version < 130) + && !state->ARB_explicit_attrib_location_enable + && !state->ARB_fragment_coord_conventions_enable) { + if (this->type->qualifier.flags.q.out) { + _mesa_glsl_error(& loc, state, + "`out' qualifier in declaration of `%s' " + "only valid for function parameters in %s.", + decl->identifier, state->version_string); + } + if (this->type->qualifier.flags.q.in) { + _mesa_glsl_error(& loc, state, + "`in' qualifier in declaration of `%s' " + "only valid for function parameters in %s.", + decl->identifier, state->version_string); + } + /* FINISHME: Test for other invalid qualifiers. */ + } + + apply_type_qualifier_to_variable(& this->type->qualifier, var, state, + & loc); + + if (this->type->qualifier.flags.q.invariant) { + if ((state->target == vertex_shader) && !(var->mode == ir_var_out || + var->mode == ir_var_inout)) { + /* FINISHME: Note that this doesn't work for invariant on + * a function signature outval + */ + _mesa_glsl_error(& loc, state, + "`%s' cannot be marked invariant, vertex shader " + "outputs only\n", var->name); + } else if ((state->target == fragment_shader) && + !(var->mode == ir_var_in || var->mode == ir_var_inout)) { + /* FINISHME: Note that this doesn't work for invariant on + * a function signature inval + */ + _mesa_glsl_error(& loc, state, + "`%s' cannot be marked invariant, fragment shader " + "inputs only\n", var->name); + } + } + + if (state->current_function != NULL) { + const char *mode = NULL; + const char *extra = ""; + + /* There is no need to check for 'inout' here because the parser will + * only allow that in function parameter lists. + */ + if (this->type->qualifier.flags.q.attribute) { + mode = "attribute"; + } else if (this->type->qualifier.flags.q.uniform) { + mode = "uniform"; + } else if (this->type->qualifier.flags.q.varying) { + mode = "varying"; + } else if (this->type->qualifier.flags.q.in) { + mode = "in"; + extra = " or in function parameter list"; + } else if (this->type->qualifier.flags.q.out) { + mode = "out"; + extra = " or in function parameter list"; + } + + if (mode) { + _mesa_glsl_error(& loc, state, + "%s variable `%s' must be declared at " + "global scope%s", + mode, var->name, extra); + } + } else if (var->mode == ir_var_in) { + var->read_only = true; + + if (state->target == vertex_shader) { + bool error_emitted = false; + + /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: + * + * "Vertex shader inputs can only be float, floating-point + * vectors, matrices, signed and unsigned integers and integer + * vectors. Vertex shader inputs can also form arrays of these + * types, but not structures." + * + * From page 31 (page 27 of the PDF) of the GLSL 1.30 spec: + * + * "Vertex shader inputs can only be float, floating-point + * vectors, matrices, signed and unsigned integers and integer + * vectors. They cannot be arrays or structures." + * + * From page 23 (page 29 of the PDF) of the GLSL 1.20 spec: + * + * "The attribute qualifier can be used only with float, + * floating-point vectors, and matrices. Attribute variables + * cannot be declared as arrays or structures." + */ + const glsl_type *check_type = var->type->is_array() + ? var->type->fields.array : var->type; + + switch (check_type->base_type) { + case GLSL_TYPE_FLOAT: + break; + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + if (state->language_version > 120) + break; + /* FALLTHROUGH */ + default: + _mesa_glsl_error(& loc, state, + "vertex shader input / attribute cannot have " + "type %s`%s'", + var->type->is_array() ? "array of " : "", + check_type->name); + error_emitted = true; + } + + if (!error_emitted && (state->language_version <= 130) + && var->type->is_array()) { + _mesa_glsl_error(& loc, state, + "vertex shader input / attribute cannot have " + "array type"); + error_emitted = true; + } + } + } + + /* Integer vertex outputs must be qualified with 'flat'. + * + * From section 4.3.6 of the GLSL 1.30 spec: + * "If a vertex output is a signed or unsigned integer or integer + * vector, then it must be qualified with the interpolation qualifier + * flat." + */ + if (state->language_version >= 130 + && state->target == vertex_shader + && state->current_function == NULL + && var->type->is_integer() + && var->mode == ir_var_out + && var->interpolation != ir_var_flat) { + + _mesa_glsl_error(&loc, state, "If a vertex output is an integer, " + "then it must be qualified with 'flat'"); + } + + + /* Interpolation qualifiers cannot be applied to 'centroid' and + * 'centroid varying'. + * + * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: + * "interpolation qualifiers may only precede the qualifiers in, + * centroid in, out, or centroid out in a declaration. They do not apply + * to the deprecated storage qualifiers varying or centroid varying." + */ + if (state->language_version >= 130 + && this->type->qualifier.has_interpolation() + && this->type->qualifier.flags.q.varying) { + + const char *i = this->type->qualifier.interpolation_string(); + assert(i != NULL); + const char *s; + if (this->type->qualifier.flags.q.centroid) + s = "centroid varying"; + else + s = "varying"; + + _mesa_glsl_error(&loc, state, + "qualifier '%s' cannot be applied to the " + "deprecated storage qualifier '%s'", i, s); + } + + + /* Interpolation qualifiers can only apply to vertex shader outputs and + * fragment shader inputs. + * + * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: + * "Outputs from a vertex shader (out) and inputs to a fragment + * shader (in) can be further qualified with one or more of these + * interpolation qualifiers" + */ + if (state->language_version >= 130 + && this->type->qualifier.has_interpolation()) { + + const char *i = this->type->qualifier.interpolation_string(); + assert(i != NULL); + + switch (state->target) { + case vertex_shader: + if (this->type->qualifier.flags.q.in) { + _mesa_glsl_error(&loc, state, + "qualifier '%s' cannot be applied to vertex " + "shader inputs", i); + } + break; + case fragment_shader: + if (this->type->qualifier.flags.q.out) { + _mesa_glsl_error(&loc, state, + "qualifier '%s' cannot be applied to fragment " + "shader outputs", i); + } + break; + default: + assert(0); + } + } + + + /* From section 4.3.4 of the GLSL 1.30 spec: + * "It is an error to use centroid in in a vertex shader." + */ + if (state->language_version >= 130 + && this->type->qualifier.flags.q.centroid + && this->type->qualifier.flags.q.in + && state->target == vertex_shader) { + + _mesa_glsl_error(&loc, state, + "'centroid in' cannot be used in a vertex shader"); + } + + + /* Precision qualifiers exists only in GLSL versions 1.00 and >= 1.30. + */ + if (this->type->specifier->precision != ast_precision_none + && state->language_version != 100 + && state->language_version < 130) { + + _mesa_glsl_error(&loc, state, + "precision qualifiers are supported only in GLSL ES " + "1.00, and GLSL 1.30 and later"); + } + + + /* Precision qualifiers only apply to floating point and integer types. + * + * From section 4.5.2 of the GLSL 1.30 spec: + * "Any floating point or any integer declaration can have the type + * preceded by one of these precision qualifiers [...] Literal + * constants do not have precision qualifiers. Neither do Boolean + * variables. + * + * In GLSL ES, sampler types are also allowed. + * + * From page 87 of the GLSL ES spec: + * "RESOLUTION: Allow sampler types to take a precision qualifier." + */ + if (this->type->specifier->precision != ast_precision_none + && !var->type->is_float() + && !var->type->is_integer() + && !(var->type->is_sampler() && state->es_shader) + && !(var->type->is_array() + && (var->type->fields.array->is_float() + || var->type->fields.array->is_integer()))) { + + _mesa_glsl_error(&loc, state, + "precision qualifiers apply only to floating point" + "%s types", state->es_shader ? ", integer, and sampler" + : "and integer"); + } + + /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: + * + * "[Sampler types] can only be declared as function + * parameters or uniform variables (see Section 4.3.5 + * "Uniform")". + */ + if (var_type->contains_sampler() && + !this->type->qualifier.flags.q.uniform) { + _mesa_glsl_error(&loc, state, "samplers must be declared uniform"); + } + + /* Process the initializer and add its instructions to a temporary + * list. This list will be added to the instruction stream (below) after + * the declaration is added. This is done because in some cases (such as + * redeclarations) the declaration may not actually be added to the + * instruction stream. + */ + exec_list initializer_instructions; + ir_variable *earlier = get_variable_being_redeclared(var, decl, state); + + if (decl->initializer != NULL) { + result = process_initializer((earlier == NULL) ? var : earlier, + decl, this->type, + &initializer_instructions, state); + } + + /* From page 23 (page 29 of the PDF) of the GLSL 1.10 spec: + * + * "It is an error to write to a const variable outside of + * its declaration, so they must be initialized when + * declared." + */ + if (this->type->qualifier.flags.q.constant && decl->initializer == NULL) { + _mesa_glsl_error(& loc, state, + "const declaration of `%s' must be initialized", + decl->identifier); + } + + /* If the declaration is not a redeclaration, there are a few additional + * semantic checks that must be applied. In addition, variable that was + * created for the declaration should be added to the IR stream. + */ + if (earlier == NULL) { + /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, + * + * "Identifiers starting with "gl_" are reserved for use by + * OpenGL, and may not be declared in a shader as either a + * variable or a function." + */ + if (strncmp(decl->identifier, "gl_", 3) == 0) + _mesa_glsl_error(& loc, state, + "identifier `%s' uses reserved `gl_' prefix", + decl->identifier); + + /* Add the variable to the symbol table. Note that the initializer's + * IR was already processed earlier (though it hasn't been emitted + * yet), without the variable in scope. + * + * This differs from most C-like languages, but it follows the GLSL + * specification. From page 28 (page 34 of the PDF) of the GLSL 1.50 + * spec: + * + * "Within a declaration, the scope of a name starts immediately + * after the initializer if present or immediately after the name + * being declared if not." + */ + if (!state->symbols->add_variable(var)) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, "name `%s' already taken in the " + "current scope", decl->identifier); + continue; + } + + /* Push the variable declaration to the top. It means that all the + * variable declarations will appear in a funny last-to-first order, + * but otherwise we run into trouble if a function is prototyped, a + * global var is decled, then the function is defined with usage of + * the global var. See glslparsertest's CorrectModule.frag. + */ + instructions->push_head(var); + } + + instructions->append_list(&initializer_instructions); + } + + + /* Generally, variable declarations do not have r-values. However, + * one is used for the declaration in + * + * while (bool b = some_condition()) { + * ... + * } + * + * so we return the rvalue from the last seen declaration here. + */ + return result; +} + + +ir_rvalue * +ast_parameter_declarator::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + const struct glsl_type *type; + const char *name = NULL; + YYLTYPE loc = this->get_location(); + + type = this->type->specifier->glsl_type(& name, state); + + if (type == NULL) { + if (name != NULL) { + _mesa_glsl_error(& loc, state, + "invalid type `%s' in declaration of `%s'", + name, this->identifier); + } else { + _mesa_glsl_error(& loc, state, + "invalid type in declaration of `%s'", + this->identifier); + } + + type = glsl_type::error_type; + } + + /* From page 62 (page 68 of the PDF) of the GLSL 1.50 spec: + * + * "Functions that accept no input arguments need not use void in the + * argument list because prototypes (or definitions) are required and + * therefore there is no ambiguity when an empty argument list "( )" is + * declared. The idiom "(void)" as a parameter list is provided for + * convenience." + * + * Placing this check here prevents a void parameter being set up + * for a function, which avoids tripping up checks for main taking + * parameters and lookups of an unnamed symbol. + */ + if (type->is_void()) { + if (this->identifier != NULL) + _mesa_glsl_error(& loc, state, + "named parameter cannot have type `void'"); + + is_void = true; + return NULL; + } + + if (formal_parameter && (this->identifier == NULL)) { + _mesa_glsl_error(& loc, state, "formal parameter lacks a name"); + return NULL; + } + + /* This only handles "vec4 foo[..]". The earlier specifier->glsl_type(...) + * call already handled the "vec4[..] foo" case. + */ + if (this->is_array) { + type = process_array_type(&loc, type, this->array_size, state); + } + + if (type->array_size() == 0) { + _mesa_glsl_error(&loc, state, "arrays passed as parameters must have " + "a declared size."); + type = glsl_type::error_type; + } + + is_void = false; + ir_variable *var = new(ctx) ir_variable(type, this->identifier, ir_var_in); + + /* Apply any specified qualifiers to the parameter declaration. Note that + * for function parameters the default mode is 'in'. + */ + apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc); + + /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: + * + * "Samplers cannot be treated as l-values; hence cannot be used + * as out or inout function parameters, nor can they be assigned + * into." + */ + if ((var->mode == ir_var_inout || var->mode == ir_var_out) + && type->contains_sampler()) { + _mesa_glsl_error(&loc, state, "out and inout parameters cannot contain samplers"); + type = glsl_type::error_type; + } + + instructions->push_tail(var); + + /* Parameter declarations do not have r-values. + */ + return NULL; +} + + +void +ast_parameter_declarator::parameters_to_hir(exec_list *ast_parameters, + bool formal, + exec_list *ir_parameters, + _mesa_glsl_parse_state *state) +{ + ast_parameter_declarator *void_param = NULL; + unsigned count = 0; + + foreach_list_typed (ast_parameter_declarator, param, link, ast_parameters) { + param->formal_parameter = formal; + param->hir(ir_parameters, state); + + if (param->is_void) + void_param = param; + + count++; + } + + if ((void_param != NULL) && (count > 1)) { + YYLTYPE loc = void_param->get_location(); + + _mesa_glsl_error(& loc, state, + "`void' parameter must be only parameter"); + } +} + + +void +emit_function(_mesa_glsl_parse_state *state, ir_function *f) +{ + /* IR invariants disallow function declarations or definitions + * nested within other function definitions. But there is no + * requirement about the relative order of function declarations + * and definitions with respect to one another. So simply insert + * the new ir_function block at the end of the toplevel instruction + * list. + */ + state->toplevel_ir->push_tail(f); +} + + +ir_rvalue * +ast_function::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + ir_function *f = NULL; + ir_function_signature *sig = NULL; + exec_list hir_parameters; + + const char *const name = identifier; + + /* New functions are always added to the top-level IR instruction stream, + * so this instruction list pointer is ignored. See also emit_function + * (called below). + */ + (void) instructions; + + /* From page 21 (page 27 of the PDF) of the GLSL 1.20 spec, + * + * "Function declarations (prototypes) cannot occur inside of functions; + * they must be at global scope, or for the built-in functions, outside + * the global scope." + * + * From page 27 (page 33 of the PDF) of the GLSL ES 1.00.16 spec, + * + * "User defined functions may only be defined within the global scope." + * + * Note that this language does not appear in GLSL 1.10. + */ + if ((state->current_function != NULL) && (state->language_version != 110)) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, + "declaration of function `%s' not allowed within " + "function body", name); + } + + /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, + * + * "Identifiers starting with "gl_" are reserved for use by + * OpenGL, and may not be declared in a shader as either a + * variable or a function." + */ + if (strncmp(name, "gl_", 3) == 0) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, + "identifier `%s' uses reserved `gl_' prefix", name); + } + + /* Convert the list of function parameters to HIR now so that they can be + * used below to compare this function's signature with previously seen + * signatures for functions with the same name. + */ + ast_parameter_declarator::parameters_to_hir(& this->parameters, + is_definition, + & hir_parameters, state); + + const char *return_type_name; + const glsl_type *return_type = + this->return_type->specifier->glsl_type(& return_type_name, state); + + if (!return_type) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, + "function `%s' has undeclared return type `%s'", + name, return_type_name); + return_type = glsl_type::error_type; + } + + /* From page 56 (page 62 of the PDF) of the GLSL 1.30 spec: + * "No qualifier is allowed on the return type of a function." + */ + if (this->return_type->has_qualifiers()) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(& loc, state, + "function `%s' return type has qualifiers", name); + } + + /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: + * + * "[Sampler types] can only be declared as function parameters + * or uniform variables (see Section 4.3.5 "Uniform")". + */ + if (return_type->contains_sampler()) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, + "function `%s' return type can't contain a sampler", + name); + } + + /* Verify that this function's signature either doesn't match a previously + * seen signature for a function with the same name, or, if a match is found, + * that the previously seen signature does not have an associated definition. + */ + f = state->symbols->get_function(name); + if (f != NULL && (state->es_shader || f->has_user_signature())) { + sig = f->exact_matching_signature(&hir_parameters); + if (sig != NULL) { + const char *badvar = sig->qualifiers_match(&hir_parameters); + if (badvar != NULL) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(&loc, state, "function `%s' parameter `%s' " + "qualifiers don't match prototype", name, badvar); + } + + if (sig->return_type != return_type) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(&loc, state, "function `%s' return type doesn't " + "match prototype", name); + } + + if (is_definition && sig->is_defined) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, "function `%s' redefined", name); + } + } + } else { + f = new(ctx) ir_function(name); + if (!state->symbols->add_function(f)) { + /* This function name shadows a non-function use of the same name. */ + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(&loc, state, "function name `%s' conflicts with " + "non-function", name); + return NULL; + } + + emit_function(state, f); + } + + /* Verify the return type of main() */ + if (strcmp(name, "main") == 0) { + if (! return_type->is_void()) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, "main() must return void"); + } + + if (!hir_parameters.is_empty()) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, "main() must not take any parameters"); + } + } + + /* Finish storing the information about this new function in its signature. + */ + if (sig == NULL) { + sig = new(ctx) ir_function_signature(return_type); + f->add_signature(sig); + } + + sig->replace_parameters(&hir_parameters); + signature = sig; + + /* Function declarations (prototypes) do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_function_definition::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + prototype->is_definition = true; + prototype->hir(instructions, state); + + ir_function_signature *signature = prototype->signature; + if (signature == NULL) + return NULL; + + assert(state->current_function == NULL); + state->current_function = signature; + state->found_return = false; + + /* Duplicate parameters declared in the prototype as concrete variables. + * Add these to the symbol table. + */ + state->symbols->push_scope(); + foreach_iter(exec_list_iterator, iter, signature->parameters) { + ir_variable *const var = ((ir_instruction *) iter.get())->as_variable(); + + assert(var != NULL); + + /* The only way a parameter would "exist" is if two parameters have + * the same name. + */ + if (state->symbols->name_declared_this_scope(var->name)) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, "parameter `%s' redeclared", var->name); + } else { + state->symbols->add_variable(var); + } + } + + /* Convert the body of the function to HIR. */ + this->body->hir(&signature->body, state); + signature->is_defined = true; + + state->symbols->pop_scope(); + + assert(state->current_function == signature); + state->current_function = NULL; + + if (!signature->return_type->is_void() && !state->found_return) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(& loc, state, "function `%s' has non-void return type " + "%s, but no return statement", + signature->function_name(), + signature->return_type->name); + } + + /* Function definitions do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_jump_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + switch (mode) { + case ast_return: { + ir_return *inst; + assert(state->current_function); + + if (opt_return_value) { + ir_rvalue *const ret = opt_return_value->hir(instructions, state); + + /* The value of the return type can be NULL if the shader says + * 'return foo();' and foo() is a function that returns void. + * + * NOTE: The GLSL spec doesn't say that this is an error. The type + * of the return value is void. If the return type of the function is + * also void, then this should compile without error. Seriously. + */ + const glsl_type *const ret_type = + (ret == NULL) ? glsl_type::void_type : ret->type; + + /* Implicit conversions are not allowed for return values. */ + if (state->current_function->return_type != ret_type) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, + "`return' with wrong type %s, in function `%s' " + "returning %s", + ret_type->name, + state->current_function->function_name(), + state->current_function->return_type->name); + } + + inst = new(ctx) ir_return(ret); + } else { + if (state->current_function->return_type->base_type != + GLSL_TYPE_VOID) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, + "`return' with no value, in function %s returning " + "non-void", + state->current_function->function_name()); + } + inst = new(ctx) ir_return; + } + + state->found_return = true; + instructions->push_tail(inst); + break; + } + + case ast_discard: + if (state->target != fragment_shader) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, + "`discard' may only appear in a fragment shader"); + } + instructions->push_tail(new(ctx) ir_discard); + break; + + case ast_break: + case ast_continue: + /* FINISHME: Handle switch-statements. They cannot contain 'continue', + * FINISHME: and they use a different IR instruction for 'break'. + */ + /* FINISHME: Correctly handle the nesting. If a switch-statement is + * FINISHME: inside a loop, a 'continue' is valid and will bind to the + * FINISHME: loop. + */ + if (state->loop_or_switch_nesting == NULL) { + YYLTYPE loc = this->get_location(); + + _mesa_glsl_error(& loc, state, + "`%s' may only appear in a loop", + (mode == ast_break) ? "break" : "continue"); + } else { + ir_loop *const loop = state->loop_or_switch_nesting->as_loop(); + + /* Inline the for loop expression again, since we don't know + * where near the end of the loop body the normal copy of it + * is going to be placed. + */ + if (mode == ast_continue && + state->loop_or_switch_nesting_ast->rest_expression) { + state->loop_or_switch_nesting_ast->rest_expression->hir(instructions, + state); + } + + if (loop != NULL) { + ir_loop_jump *const jump = + new(ctx) ir_loop_jump((mode == ast_break) + ? ir_loop_jump::jump_break + : ir_loop_jump::jump_continue); + instructions->push_tail(jump); + } + } + + break; + } + + /* Jump instructions do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_selection_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + ir_rvalue *const condition = this->condition->hir(instructions, state); + + /* From page 66 (page 72 of the PDF) of the GLSL 1.50 spec: + * + * "Any expression whose type evaluates to a Boolean can be used as the + * conditional expression bool-expression. Vector types are not accepted + * as the expression to if." + * + * The checks are separated so that higher quality diagnostics can be + * generated for cases where both rules are violated. + */ + if (!condition->type->is_boolean() || !condition->type->is_scalar()) { + YYLTYPE loc = this->condition->get_location(); + + _mesa_glsl_error(& loc, state, "if-statement condition must be scalar " + "boolean"); + } + + ir_if *const stmt = new(ctx) ir_if(condition); + + if (then_statement != NULL) { + state->symbols->push_scope(); + then_statement->hir(& stmt->then_instructions, state); + state->symbols->pop_scope(); + } + + if (else_statement != NULL) { + state->symbols->push_scope(); + else_statement->hir(& stmt->else_instructions, state); + state->symbols->pop_scope(); + } + + instructions->push_tail(stmt); + + /* if-statements do not have r-values. + */ + return NULL; +} + + +void +ast_iteration_statement::condition_to_hir(ir_loop *stmt, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + if (condition != NULL) { + ir_rvalue *const cond = + condition->hir(& stmt->body_instructions, state); + + if ((cond == NULL) + || !cond->type->is_boolean() || !cond->type->is_scalar()) { + YYLTYPE loc = condition->get_location(); + + _mesa_glsl_error(& loc, state, + "loop condition must be scalar boolean"); + } else { + /* As the first code in the loop body, generate a block that looks + * like 'if (!condition) break;' as the loop termination condition. + */ + ir_rvalue *const not_cond = + new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond, + NULL); + + ir_if *const if_stmt = new(ctx) ir_if(not_cond); + + ir_jump *const break_stmt = + new(ctx) ir_loop_jump(ir_loop_jump::jump_break); + + if_stmt->then_instructions.push_tail(break_stmt); + stmt->body_instructions.push_tail(if_stmt); + } + } +} + + +ir_rvalue * +ast_iteration_statement::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + + /* For-loops and while-loops start a new scope, but do-while loops do not. + */ + if (mode != ast_do_while) + state->symbols->push_scope(); + + if (init_statement != NULL) + init_statement->hir(instructions, state); + + ir_loop *const stmt = new(ctx) ir_loop(); + instructions->push_tail(stmt); + + /* Track the current loop and / or switch-statement nesting. + */ + ir_instruction *const nesting = state->loop_or_switch_nesting; + ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast; + + state->loop_or_switch_nesting = stmt; + state->loop_or_switch_nesting_ast = this; + + if (mode != ast_do_while) + condition_to_hir(stmt, state); + + if (body != NULL) + body->hir(& stmt->body_instructions, state); + + if (rest_expression != NULL) + rest_expression->hir(& stmt->body_instructions, state); + + if (mode == ast_do_while) + condition_to_hir(stmt, state); + + if (mode != ast_do_while) + state->symbols->pop_scope(); + + /* Restore previous nesting before returning. + */ + state->loop_or_switch_nesting = nesting; + state->loop_or_switch_nesting_ast = nesting_ast; + + /* Loops do not have r-values. + */ + return NULL; +} + + +ir_rvalue * +ast_type_specifier::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + if (!this->is_precision_statement && this->structure == NULL) + return NULL; + + YYLTYPE loc = this->get_location(); + + if (this->precision != ast_precision_none + && state->language_version != 100 + && state->language_version < 130) { + _mesa_glsl_error(&loc, state, + "precision qualifiers exist only in " + "GLSL ES 1.00, and GLSL 1.30 and later"); + return NULL; + } + if (this->precision != ast_precision_none + && this->structure != NULL) { + _mesa_glsl_error(&loc, state, + "precision qualifiers do not apply to structures"); + return NULL; + } + + /* If this is a precision statement, check that the type to which it is + * applied is either float or int. + * + * From section 4.5.3 of the GLSL 1.30 spec: + * "The precision statement + * precision precision-qualifier type; + * can be used to establish a default precision qualifier. The type + * field can be either int or float [...]. Any other types or + * qualifiers will result in an error. + */ + if (this->is_precision_statement) { + assert(this->precision != ast_precision_none); + assert(this->structure == NULL); /* The check for structures was + * performed above. */ + if (this->is_array) { + _mesa_glsl_error(&loc, state, + "default precision statements do not apply to " + "arrays"); + return NULL; + } + if (this->type_specifier != ast_float + && this->type_specifier != ast_int) { + _mesa_glsl_error(&loc, state, + "default precision statements apply only to types " + "float and int"); + return NULL; + } + + /* FINISHME: Translate precision statements into IR. */ + return NULL; + } + + if (this->structure != NULL) + return this->structure->hir(instructions, state); + + return NULL; +} + + +ir_rvalue * +ast_struct_specifier::hir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + unsigned decl_count = 0; + + /* Make an initial pass over the list of structure fields to determine how + * many there are. Each element in this list is an ast_declarator_list. + * This means that we actually need to count the number of elements in the + * 'declarations' list in each of the elements. + */ + foreach_list_typed (ast_declarator_list, decl_list, link, + &this->declarations) { + foreach_list_const (decl_ptr, & decl_list->declarations) { + decl_count++; + } + } + + /* Allocate storage for the structure fields and process the field + * declarations. As the declarations are processed, try to also convert + * the types to HIR. This ensures that structure definitions embedded in + * other structure definitions are processed. + */ + glsl_struct_field *const fields = ralloc_array(state, glsl_struct_field, + decl_count); + + unsigned i = 0; + foreach_list_typed (ast_declarator_list, decl_list, link, + &this->declarations) { + const char *type_name; + + decl_list->type->specifier->hir(instructions, state); + + /* Section 10.9 of the GLSL ES 1.00 specification states that + * embedded structure definitions have been removed from the language. + */ + if (state->es_shader && decl_list->type->specifier->structure != NULL) { + YYLTYPE loc = this->get_location(); + _mesa_glsl_error(&loc, state, "Embedded structure definitions are " + "not allowed in GLSL ES 1.00."); + } + + const glsl_type *decl_type = + decl_list->type->specifier->glsl_type(& type_name, state); + + foreach_list_typed (ast_declaration, decl, link, + &decl_list->declarations) { + const struct glsl_type *field_type = decl_type; + if (decl->is_array) { + YYLTYPE loc = decl->get_location(); + field_type = process_array_type(&loc, decl_type, decl->array_size, + state); + } + fields[i].type = (field_type != NULL) + ? field_type : glsl_type::error_type; + fields[i].name = decl->identifier; + i++; + } + } + + assert(i == decl_count); + + const glsl_type *t = + glsl_type::get_record_instance(fields, decl_count, this->name); + + YYLTYPE loc = this->get_location(); + if (!state->symbols->add_type(name, t)) { + _mesa_glsl_error(& loc, state, "struct `%s' previously defined", name); + } else { + const glsl_type **s = reralloc(state, state->user_structures, + const glsl_type *, + state->num_user_structures + 1); + if (s != NULL) { + s[state->num_user_structures] = t; + state->user_structures = s; + state->num_user_structures++; + } + } + + /* Structure type definitions do not have r-values. + */ + return NULL; +} diff --git a/mesalib/src/glsl/ast_type.cpp b/mesalib/src/glsl/ast_type.cpp index 8e49bef8a..c680ae5f6 100644 --- a/mesalib/src/glsl/ast_type.cpp +++ b/mesalib/src/glsl/ast_type.cpp @@ -1,138 +1,138 @@ -/* - * 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 "ast.h" -extern "C" { -#include "program/symbol_table.h" -} - -void -ast_type_specifier::print(void) const -{ - if (type_specifier == ast_struct) { - structure->print(); - } else { - printf("%s ", type_name); - } - - if (is_array) { - printf("[ "); - - if (array_size) { - array_size->print(); - } - - printf("] "); - } -} - -ast_type_specifier::ast_type_specifier(int specifier) - : type_specifier(ast_types(specifier)), type_name(NULL), structure(NULL), - is_array(false), array_size(NULL), precision(ast_precision_none), - is_precision_statement(false) -{ - static const char *const names[] = { - "void", - "float", - "int", - "uint", - "bool", - "vec2", - "vec3", - "vec4", - "bvec2", - "bvec3", - "bvec4", - "ivec2", - "ivec3", - "ivec4", - "uvec2", - "uvec3", - "uvec4", - "mat2", - "mat2x3", - "mat2x4", - "mat3x2", - "mat3", - "mat3x4", - "mat4x2", - "mat4x3", - "mat4", - "sampler1D", - "sampler2D", - "sampler2DRect", - "sampler3D", - "samplerCube", - "sampler1DShadow", - "sampler2DShadow", - "sampler2DRectShadow", - "samplerCubeShadow", - "sampler1DArray", - "sampler2DArray", - "sampler1DArrayShadow", - "sampler2DArrayShadow", - "isampler1D", - "isampler2D", - "isampler3D", - "isamplerCube", - "isampler1DArray", - "isampler2DArray", - "usampler1D", - "usampler2D", - "usampler3D", - "usamplerCube", - "usampler1DArray", - "usampler2DArray", - - NULL, /* ast_struct */ - NULL /* ast_type_name */ - }; - - type_name = names[specifier]; -} - -bool -ast_fully_specified_type::has_qualifiers() const -{ - return this->qualifier.flags.i != 0; -} - -bool ast_type_qualifier::has_interpolation() const -{ - return this->flags.q.smooth - || this->flags.q.flat - || this->flags.q.noperspective; -} - -const char* -ast_type_qualifier::interpolation_string() const -{ - if (this->flags.q.smooth) - return "smooth"; - else if (this->flags.q.flat) - return "flat"; - else if (this->flags.q.noperspective) - return "noperspective"; - else - return NULL; -} +/* + * 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 "ast.h" +extern "C" { +#include "program/symbol_table.h" +} + +void +ast_type_specifier::print(void) const +{ + if (type_specifier == ast_struct) { + structure->print(); + } else { + printf("%s ", type_name); + } + + if (is_array) { + printf("[ "); + + if (array_size) { + array_size->print(); + } + + printf("] "); + } +} + +ast_type_specifier::ast_type_specifier(int specifier) + : type_specifier(ast_types(specifier)), type_name(NULL), structure(NULL), + is_array(false), array_size(NULL), precision(ast_precision_none), + is_precision_statement(false) +{ + static const char *const names[] = { + "void", + "float", + "int", + "uint", + "bool", + "vec2", + "vec3", + "vec4", + "bvec2", + "bvec3", + "bvec4", + "ivec2", + "ivec3", + "ivec4", + "uvec2", + "uvec3", + "uvec4", + "mat2", + "mat2x3", + "mat2x4", + "mat3x2", + "mat3", + "mat3x4", + "mat4x2", + "mat4x3", + "mat4", + "sampler1D", + "sampler2D", + "sampler2DRect", + "sampler3D", + "samplerCube", + "sampler1DShadow", + "sampler2DShadow", + "sampler2DRectShadow", + "samplerCubeShadow", + "sampler1DArray", + "sampler2DArray", + "sampler1DArrayShadow", + "sampler2DArrayShadow", + "isampler1D", + "isampler2D", + "isampler3D", + "isamplerCube", + "isampler1DArray", + "isampler2DArray", + "usampler1D", + "usampler2D", + "usampler3D", + "usamplerCube", + "usampler1DArray", + "usampler2DArray", + + NULL, /* ast_struct */ + NULL /* ast_type_name */ + }; + + type_name = names[specifier]; +} + +bool +ast_fully_specified_type::has_qualifiers() const +{ + return this->qualifier.flags.i != 0; +} + +bool ast_type_qualifier::has_interpolation() const +{ + return this->flags.q.smooth + || this->flags.q.flat + || this->flags.q.noperspective; +} + +const char* +ast_type_qualifier::interpolation_string() const +{ + if (this->flags.q.smooth) + return "smooth"; + else if (this->flags.q.flat) + return "flat"; + else if (this->flags.q.noperspective) + return "noperspective"; + else + return NULL; +} diff --git a/mesalib/src/glsl/builtin_types.h b/mesalib/src/glsl/builtin_types.h index 6b98419ae..58b9a8127 100644 --- a/mesalib/src/glsl/builtin_types.h +++ b/mesalib/src/glsl/builtin_types.h @@ -1,302 +1,302 @@ -/* - * Copyright © 2009 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. - */ - -const glsl_type glsl_type::_error_type = - glsl_type(GL_INVALID_ENUM, GLSL_TYPE_ERROR, 0, 0, ""); - -const glsl_type glsl_type::_void_type = - glsl_type(GL_INVALID_ENUM, GLSL_TYPE_VOID, 0, 0, "void"); - -const glsl_type glsl_type::_sampler3D_type = - glsl_type(GL_SAMPLER_3D, GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_FLOAT, - "sampler3D"); - -const glsl_type *const glsl_type::error_type = & glsl_type::_error_type; -const glsl_type *const glsl_type::void_type = & glsl_type::_void_type; - -/** \name Core built-in types - * - * These types exist in all versions of GLSL. - */ -/*@{*/ - -const glsl_type glsl_type::builtin_core_types[] = { - glsl_type(GL_BOOL, GLSL_TYPE_BOOL, 1, 1, "bool"), - glsl_type(GL_BOOL_VEC2, GLSL_TYPE_BOOL, 2, 1, "bvec2"), - glsl_type(GL_BOOL_VEC3, GLSL_TYPE_BOOL, 3, 1, "bvec3"), - glsl_type(GL_BOOL_VEC4, GLSL_TYPE_BOOL, 4, 1, "bvec4"), - glsl_type(GL_INT, GLSL_TYPE_INT, 1, 1, "int"), - glsl_type(GL_INT_VEC2, GLSL_TYPE_INT, 2, 1, "ivec2"), - glsl_type(GL_INT_VEC3, GLSL_TYPE_INT, 3, 1, "ivec3"), - glsl_type(GL_INT_VEC4, GLSL_TYPE_INT, 4, 1, "ivec4"), - glsl_type(GL_FLOAT, GLSL_TYPE_FLOAT, 1, 1, "float"), - glsl_type(GL_FLOAT_VEC2, GLSL_TYPE_FLOAT, 2, 1, "vec2"), - glsl_type(GL_FLOAT_VEC3, GLSL_TYPE_FLOAT, 3, 1, "vec3"), - glsl_type(GL_FLOAT_VEC4, GLSL_TYPE_FLOAT, 4, 1, "vec4"), - glsl_type(GL_FLOAT_MAT2, GLSL_TYPE_FLOAT, 2, 2, "mat2"), - glsl_type(GL_FLOAT_MAT3, GLSL_TYPE_FLOAT, 3, 3, "mat3"), - glsl_type(GL_FLOAT_MAT4, GLSL_TYPE_FLOAT, 4, 4, "mat4"), - glsl_type(GL_SAMPLER_2D, GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_FLOAT, - "sampler2D"), - glsl_type(GL_SAMPLER_CUBE, GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_FLOAT, - "samplerCube"), -}; - -const glsl_type *const glsl_type::bool_type = & builtin_core_types[0]; -const glsl_type *const glsl_type::int_type = & builtin_core_types[4]; -const glsl_type *const glsl_type::ivec4_type = & builtin_core_types[7]; -const glsl_type *const glsl_type::float_type = & builtin_core_types[8]; -const glsl_type *const glsl_type::vec2_type = & builtin_core_types[9]; -const glsl_type *const glsl_type::vec3_type = & builtin_core_types[10]; -const glsl_type *const glsl_type::vec4_type = & builtin_core_types[11]; -const glsl_type *const glsl_type::mat2_type = & builtin_core_types[12]; -const glsl_type *const glsl_type::mat3_type = & builtin_core_types[13]; -const glsl_type *const glsl_type::mat4_type = & builtin_core_types[14]; -/*@}*/ - -/** \name GLSL structures that have not been deprecated. - */ -/*@{*/ - -static const struct glsl_struct_field gl_DepthRangeParameters_fields[] = { - { glsl_type::float_type, "near" }, - { glsl_type::float_type, "far" }, - { glsl_type::float_type, "diff" }, -}; - -const glsl_type glsl_type::builtin_structure_types[] = { - glsl_type(gl_DepthRangeParameters_fields, - Elements(gl_DepthRangeParameters_fields), - "gl_DepthRangeParameters"), -}; -/*@}*/ - -/** \name GLSL 1.00 / 1.10 structures that are deprecated in GLSL 1.30 - */ -/*@{*/ - -static const struct glsl_struct_field gl_PointParameters_fields[] = { - { glsl_type::float_type, "size" }, - { glsl_type::float_type, "sizeMin" }, - { glsl_type::float_type, "sizeMax" }, - { glsl_type::float_type, "fadeThresholdSize" }, - { glsl_type::float_type, "distanceConstantAttenuation" }, - { glsl_type::float_type, "distanceLinearAttenuation" }, - { glsl_type::float_type, "distanceQuadraticAttenuation" }, -}; - -static const struct glsl_struct_field gl_MaterialParameters_fields[] = { - { glsl_type::vec4_type, "emission" }, - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, - { glsl_type::float_type, "shininess" }, -}; - -static const struct glsl_struct_field gl_LightSourceParameters_fields[] = { - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, - { glsl_type::vec4_type, "position" }, - { glsl_type::vec4_type, "halfVector" }, - { glsl_type::vec3_type, "spotDirection" }, - { glsl_type::float_type, "spotExponent" }, - { glsl_type::float_type, "spotCutoff" }, - { glsl_type::float_type, "spotCosCutoff" }, - { glsl_type::float_type, "constantAttenuation" }, - { glsl_type::float_type, "linearAttenuation" }, - { glsl_type::float_type, "quadraticAttenuation" }, -}; - -static const struct glsl_struct_field gl_LightModelParameters_fields[] = { - { glsl_type::vec4_type, "ambient" }, -}; - -static const struct glsl_struct_field gl_LightModelProducts_fields[] = { - { glsl_type::vec4_type, "sceneColor" }, -}; - -static const struct glsl_struct_field gl_LightProducts_fields[] = { - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, -}; - -static const struct glsl_struct_field gl_FogParameters_fields[] = { - { glsl_type::vec4_type, "color" }, - { glsl_type::float_type, "density" }, - { glsl_type::float_type, "start" }, - { glsl_type::float_type, "end" }, - { glsl_type::float_type, "scale" }, -}; - -const glsl_type glsl_type::builtin_110_deprecated_structure_types[] = { - glsl_type(gl_PointParameters_fields, - Elements(gl_PointParameters_fields), - "gl_PointParameters"), - glsl_type(gl_MaterialParameters_fields, - Elements(gl_MaterialParameters_fields), - "gl_MaterialParameters"), - glsl_type(gl_LightSourceParameters_fields, - Elements(gl_LightSourceParameters_fields), - "gl_LightSourceParameters"), - glsl_type(gl_LightModelParameters_fields, - Elements(gl_LightModelParameters_fields), - "gl_LightModelParameters"), - glsl_type(gl_LightModelProducts_fields, - Elements(gl_LightModelProducts_fields), - "gl_LightModelProducts"), - glsl_type(gl_LightProducts_fields, - Elements(gl_LightProducts_fields), - "gl_LightProducts"), - glsl_type(gl_FogParameters_fields, - Elements(gl_FogParameters_fields), - "gl_FogParameters"), -}; -/*@}*/ - -/** \name Types in GLSL 1.10 (but not GLSL ES 1.00) - */ -/*@{*/ -const glsl_type glsl_type::builtin_110_types[] = { - glsl_type(GL_SAMPLER_1D, GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_FLOAT, - "sampler1D"), - glsl_type(GL_SAMPLER_1D_SHADOW, GLSL_SAMPLER_DIM_1D, 1, 0, GLSL_TYPE_FLOAT, - "sampler1DShadow"), - glsl_type(GL_SAMPLER_2D_SHADOW, GLSL_SAMPLER_DIM_2D, 1, 0, GLSL_TYPE_FLOAT, - "sampler2DShadow"), -}; -/*@}*/ - -/** \name Types added in GLSL 1.20 - */ -/*@{*/ - -const glsl_type glsl_type::builtin_120_types[] = { - glsl_type(GL_FLOAT_MAT2x3, GLSL_TYPE_FLOAT, 3, 2, "mat2x3"), - glsl_type(GL_FLOAT_MAT2x4, GLSL_TYPE_FLOAT, 4, 2, "mat2x4"), - glsl_type(GL_FLOAT_MAT3x2, GLSL_TYPE_FLOAT, 2, 3, "mat3x2"), - glsl_type(GL_FLOAT_MAT3x4, GLSL_TYPE_FLOAT, 4, 3, "mat3x4"), - glsl_type(GL_FLOAT_MAT4x2, GLSL_TYPE_FLOAT, 2, 4, "mat4x2"), - glsl_type(GL_FLOAT_MAT4x3, GLSL_TYPE_FLOAT, 3, 4, "mat4x3"), -}; -const glsl_type *const glsl_type::mat2x3_type = & builtin_120_types[0]; -const glsl_type *const glsl_type::mat2x4_type = & builtin_120_types[1]; -const glsl_type *const glsl_type::mat3x2_type = & builtin_120_types[2]; -const glsl_type *const glsl_type::mat3x4_type = & builtin_120_types[3]; -const glsl_type *const glsl_type::mat4x2_type = & builtin_120_types[4]; -const glsl_type *const glsl_type::mat4x3_type = & builtin_120_types[5]; -/*@}*/ - -/** \name Types added in GLSL 1.30 - */ -/*@{*/ - -const glsl_type glsl_type::builtin_130_types[] = { - glsl_type(GL_UNSIGNED_INT, GLSL_TYPE_UINT, 1, 1, "uint"), - glsl_type(GL_UNSIGNED_INT_VEC2, GLSL_TYPE_UINT, 2, 1, "uvec2"), - glsl_type(GL_UNSIGNED_INT_VEC3, GLSL_TYPE_UINT, 3, 1, "uvec3"), - glsl_type(GL_UNSIGNED_INT_VEC4, GLSL_TYPE_UINT, 4, 1, "uvec4"), - - /* 1D and 2D texture arrays - several of these are included only in - * builtin_EXT_texture_array_types. - */ - glsl_type(GL_INT_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_INT, "isampler1DArray"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_UINT, "usampler1DArray"), - glsl_type(GL_INT_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_INT, "isampler2DArray"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_UINT, "usampler2DArray"), - - /* cube shadow samplers */ - glsl_type(GL_SAMPLER_CUBE_SHADOW, - GLSL_SAMPLER_DIM_CUBE, 1, 0, GLSL_TYPE_FLOAT, "samplerCubeShadow"), - - /* signed and unsigned integer samplers */ - glsl_type(GL_INT_SAMPLER_1D, - GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_INT, "isampler1D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_1D, - GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_UINT, "usampler1D"), - glsl_type(GL_INT_SAMPLER_2D, - GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_INT, "isampler2D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_2D, - GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_UINT, "usampler2D"), - glsl_type(GL_INT_SAMPLER_3D, - GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_INT, "isampler3D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_3D, - GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_UINT, "usampler3D"), - glsl_type(GL_INT_SAMPLER_CUBE, - GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_INT, "isamplerCube"), - glsl_type(GL_INT_SAMPLER_CUBE, - GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_UINT, "usamplerCube"), -}; - -const glsl_type *const glsl_type::uint_type = & builtin_130_types[0]; -const glsl_type *const glsl_type::uvec2_type = & builtin_130_types[1]; -const glsl_type *const glsl_type::uvec3_type = & builtin_130_types[2]; -const glsl_type *const glsl_type::uvec4_type = & builtin_130_types[3]; -/*@}*/ - -/** \name Sampler types added by GL_ARB_texture_rectangle - */ -/*@{*/ - -const glsl_type glsl_type::builtin_ARB_texture_rectangle_types[] = { - glsl_type(GL_SAMPLER_2D_RECT, - GLSL_SAMPLER_DIM_RECT, 0, 0, GLSL_TYPE_FLOAT, "sampler2DRect"), - glsl_type(GL_SAMPLER_2D_RECT_SHADOW, - GLSL_SAMPLER_DIM_RECT, 1, 0, GLSL_TYPE_FLOAT, "sampler2DRectShadow"), -}; -/*@}*/ - -/** \name Sampler types added by GL_EXT_texture_array - */ -/*@{*/ - -const glsl_type glsl_type::builtin_EXT_texture_array_types[] = { - glsl_type(GL_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_FLOAT, "sampler1DArray"), - glsl_type(GL_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_FLOAT, "sampler2DArray"), - glsl_type(GL_SAMPLER_1D_ARRAY_SHADOW, - GLSL_SAMPLER_DIM_1D, 1, 1, GLSL_TYPE_FLOAT, "sampler1DArrayShadow"), - glsl_type(GL_SAMPLER_2D_ARRAY_SHADOW, - GLSL_SAMPLER_DIM_2D, 1, 1, GLSL_TYPE_FLOAT, "sampler2DArrayShadow"), -}; -/*@}*/ - -/** \name Sampler types added by GL_EXT_texture_buffer_object - */ -/*@{*/ - -const glsl_type glsl_type::builtin_EXT_texture_buffer_object_types[] = { - glsl_type(GL_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_FLOAT, "samplerBuffer"), - glsl_type(GL_INT_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_INT, "isamplerBuffer"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_UINT, "usamplerBuffer"), -}; -/*@}*/ +/* + * Copyright © 2009 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. + */ + +const glsl_type glsl_type::_error_type = + glsl_type(GL_INVALID_ENUM, GLSL_TYPE_ERROR, 0, 0, ""); + +const glsl_type glsl_type::_void_type = + glsl_type(GL_INVALID_ENUM, GLSL_TYPE_VOID, 0, 0, "void"); + +const glsl_type glsl_type::_sampler3D_type = + glsl_type(GL_SAMPLER_3D, GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_FLOAT, + "sampler3D"); + +const glsl_type *const glsl_type::error_type = & glsl_type::_error_type; +const glsl_type *const glsl_type::void_type = & glsl_type::_void_type; + +/** \name Core built-in types + * + * These types exist in all versions of GLSL. + */ +/*@{*/ + +const glsl_type glsl_type::builtin_core_types[] = { + glsl_type(GL_BOOL, GLSL_TYPE_BOOL, 1, 1, "bool"), + glsl_type(GL_BOOL_VEC2, GLSL_TYPE_BOOL, 2, 1, "bvec2"), + glsl_type(GL_BOOL_VEC3, GLSL_TYPE_BOOL, 3, 1, "bvec3"), + glsl_type(GL_BOOL_VEC4, GLSL_TYPE_BOOL, 4, 1, "bvec4"), + glsl_type(GL_INT, GLSL_TYPE_INT, 1, 1, "int"), + glsl_type(GL_INT_VEC2, GLSL_TYPE_INT, 2, 1, "ivec2"), + glsl_type(GL_INT_VEC3, GLSL_TYPE_INT, 3, 1, "ivec3"), + glsl_type(GL_INT_VEC4, GLSL_TYPE_INT, 4, 1, "ivec4"), + glsl_type(GL_FLOAT, GLSL_TYPE_FLOAT, 1, 1, "float"), + glsl_type(GL_FLOAT_VEC2, GLSL_TYPE_FLOAT, 2, 1, "vec2"), + glsl_type(GL_FLOAT_VEC3, GLSL_TYPE_FLOAT, 3, 1, "vec3"), + glsl_type(GL_FLOAT_VEC4, GLSL_TYPE_FLOAT, 4, 1, "vec4"), + glsl_type(GL_FLOAT_MAT2, GLSL_TYPE_FLOAT, 2, 2, "mat2"), + glsl_type(GL_FLOAT_MAT3, GLSL_TYPE_FLOAT, 3, 3, "mat3"), + glsl_type(GL_FLOAT_MAT4, GLSL_TYPE_FLOAT, 4, 4, "mat4"), + glsl_type(GL_SAMPLER_2D, GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_FLOAT, + "sampler2D"), + glsl_type(GL_SAMPLER_CUBE, GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_FLOAT, + "samplerCube"), +}; + +const glsl_type *const glsl_type::bool_type = & builtin_core_types[0]; +const glsl_type *const glsl_type::int_type = & builtin_core_types[4]; +const glsl_type *const glsl_type::ivec4_type = & builtin_core_types[7]; +const glsl_type *const glsl_type::float_type = & builtin_core_types[8]; +const glsl_type *const glsl_type::vec2_type = & builtin_core_types[9]; +const glsl_type *const glsl_type::vec3_type = & builtin_core_types[10]; +const glsl_type *const glsl_type::vec4_type = & builtin_core_types[11]; +const glsl_type *const glsl_type::mat2_type = & builtin_core_types[12]; +const glsl_type *const glsl_type::mat3_type = & builtin_core_types[13]; +const glsl_type *const glsl_type::mat4_type = & builtin_core_types[14]; +/*@}*/ + +/** \name GLSL structures that have not been deprecated. + */ +/*@{*/ + +static const struct glsl_struct_field gl_DepthRangeParameters_fields[] = { + { glsl_type::float_type, "near" }, + { glsl_type::float_type, "far" }, + { glsl_type::float_type, "diff" }, +}; + +const glsl_type glsl_type::builtin_structure_types[] = { + glsl_type(gl_DepthRangeParameters_fields, + Elements(gl_DepthRangeParameters_fields), + "gl_DepthRangeParameters"), +}; +/*@}*/ + +/** \name GLSL 1.00 / 1.10 structures that are deprecated in GLSL 1.30 + */ +/*@{*/ + +static const struct glsl_struct_field gl_PointParameters_fields[] = { + { glsl_type::float_type, "size" }, + { glsl_type::float_type, "sizeMin" }, + { glsl_type::float_type, "sizeMax" }, + { glsl_type::float_type, "fadeThresholdSize" }, + { glsl_type::float_type, "distanceConstantAttenuation" }, + { glsl_type::float_type, "distanceLinearAttenuation" }, + { glsl_type::float_type, "distanceQuadraticAttenuation" }, +}; + +static const struct glsl_struct_field gl_MaterialParameters_fields[] = { + { glsl_type::vec4_type, "emission" }, + { glsl_type::vec4_type, "ambient" }, + { glsl_type::vec4_type, "diffuse" }, + { glsl_type::vec4_type, "specular" }, + { glsl_type::float_type, "shininess" }, +}; + +static const struct glsl_struct_field gl_LightSourceParameters_fields[] = { + { glsl_type::vec4_type, "ambient" }, + { glsl_type::vec4_type, "diffuse" }, + { glsl_type::vec4_type, "specular" }, + { glsl_type::vec4_type, "position" }, + { glsl_type::vec4_type, "halfVector" }, + { glsl_type::vec3_type, "spotDirection" }, + { glsl_type::float_type, "spotExponent" }, + { glsl_type::float_type, "spotCutoff" }, + { glsl_type::float_type, "spotCosCutoff" }, + { glsl_type::float_type, "constantAttenuation" }, + { glsl_type::float_type, "linearAttenuation" }, + { glsl_type::float_type, "quadraticAttenuation" }, +}; + +static const struct glsl_struct_field gl_LightModelParameters_fields[] = { + { glsl_type::vec4_type, "ambient" }, +}; + +static const struct glsl_struct_field gl_LightModelProducts_fields[] = { + { glsl_type::vec4_type, "sceneColor" }, +}; + +static const struct glsl_struct_field gl_LightProducts_fields[] = { + { glsl_type::vec4_type, "ambient" }, + { glsl_type::vec4_type, "diffuse" }, + { glsl_type::vec4_type, "specular" }, +}; + +static const struct glsl_struct_field gl_FogParameters_fields[] = { + { glsl_type::vec4_type, "color" }, + { glsl_type::float_type, "density" }, + { glsl_type::float_type, "start" }, + { glsl_type::float_type, "end" }, + { glsl_type::float_type, "scale" }, +}; + +const glsl_type glsl_type::builtin_110_deprecated_structure_types[] = { + glsl_type(gl_PointParameters_fields, + Elements(gl_PointParameters_fields), + "gl_PointParameters"), + glsl_type(gl_MaterialParameters_fields, + Elements(gl_MaterialParameters_fields), + "gl_MaterialParameters"), + glsl_type(gl_LightSourceParameters_fields, + Elements(gl_LightSourceParameters_fields), + "gl_LightSourceParameters"), + glsl_type(gl_LightModelParameters_fields, + Elements(gl_LightModelParameters_fields), + "gl_LightModelParameters"), + glsl_type(gl_LightModelProducts_fields, + Elements(gl_LightModelProducts_fields), + "gl_LightModelProducts"), + glsl_type(gl_LightProducts_fields, + Elements(gl_LightProducts_fields), + "gl_LightProducts"), + glsl_type(gl_FogParameters_fields, + Elements(gl_FogParameters_fields), + "gl_FogParameters"), +}; +/*@}*/ + +/** \name Types in GLSL 1.10 (but not GLSL ES 1.00) + */ +/*@{*/ +const glsl_type glsl_type::builtin_110_types[] = { + glsl_type(GL_SAMPLER_1D, GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_FLOAT, + "sampler1D"), + glsl_type(GL_SAMPLER_1D_SHADOW, GLSL_SAMPLER_DIM_1D, 1, 0, GLSL_TYPE_FLOAT, + "sampler1DShadow"), + glsl_type(GL_SAMPLER_2D_SHADOW, GLSL_SAMPLER_DIM_2D, 1, 0, GLSL_TYPE_FLOAT, + "sampler2DShadow"), +}; +/*@}*/ + +/** \name Types added in GLSL 1.20 + */ +/*@{*/ + +const glsl_type glsl_type::builtin_120_types[] = { + glsl_type(GL_FLOAT_MAT2x3, GLSL_TYPE_FLOAT, 3, 2, "mat2x3"), + glsl_type(GL_FLOAT_MAT2x4, GLSL_TYPE_FLOAT, 4, 2, "mat2x4"), + glsl_type(GL_FLOAT_MAT3x2, GLSL_TYPE_FLOAT, 2, 3, "mat3x2"), + glsl_type(GL_FLOAT_MAT3x4, GLSL_TYPE_FLOAT, 4, 3, "mat3x4"), + glsl_type(GL_FLOAT_MAT4x2, GLSL_TYPE_FLOAT, 2, 4, "mat4x2"), + glsl_type(GL_FLOAT_MAT4x3, GLSL_TYPE_FLOAT, 3, 4, "mat4x3"), +}; +const glsl_type *const glsl_type::mat2x3_type = & builtin_120_types[0]; +const glsl_type *const glsl_type::mat2x4_type = & builtin_120_types[1]; +const glsl_type *const glsl_type::mat3x2_type = & builtin_120_types[2]; +const glsl_type *const glsl_type::mat3x4_type = & builtin_120_types[3]; +const glsl_type *const glsl_type::mat4x2_type = & builtin_120_types[4]; +const glsl_type *const glsl_type::mat4x3_type = & builtin_120_types[5]; +/*@}*/ + +/** \name Types added in GLSL 1.30 + */ +/*@{*/ + +const glsl_type glsl_type::builtin_130_types[] = { + glsl_type(GL_UNSIGNED_INT, GLSL_TYPE_UINT, 1, 1, "uint"), + glsl_type(GL_UNSIGNED_INT_VEC2, GLSL_TYPE_UINT, 2, 1, "uvec2"), + glsl_type(GL_UNSIGNED_INT_VEC3, GLSL_TYPE_UINT, 3, 1, "uvec3"), + glsl_type(GL_UNSIGNED_INT_VEC4, GLSL_TYPE_UINT, 4, 1, "uvec4"), + + /* 1D and 2D texture arrays - several of these are included only in + * builtin_EXT_texture_array_types. + */ + glsl_type(GL_INT_SAMPLER_1D_ARRAY, + GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_INT, "isampler1DArray"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY, + GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_UINT, "usampler1DArray"), + glsl_type(GL_INT_SAMPLER_2D_ARRAY, + GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_INT, "isampler2DArray"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, + GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_UINT, "usampler2DArray"), + + /* cube shadow samplers */ + glsl_type(GL_SAMPLER_CUBE_SHADOW, + GLSL_SAMPLER_DIM_CUBE, 1, 0, GLSL_TYPE_FLOAT, "samplerCubeShadow"), + + /* signed and unsigned integer samplers */ + glsl_type(GL_INT_SAMPLER_1D, + GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_INT, "isampler1D"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_1D, + GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_UINT, "usampler1D"), + glsl_type(GL_INT_SAMPLER_2D, + GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_INT, "isampler2D"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_2D, + GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_UINT, "usampler2D"), + glsl_type(GL_INT_SAMPLER_3D, + GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_INT, "isampler3D"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_3D, + GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_UINT, "usampler3D"), + glsl_type(GL_INT_SAMPLER_CUBE, + GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_INT, "isamplerCube"), + glsl_type(GL_INT_SAMPLER_CUBE, + GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_UINT, "usamplerCube"), +}; + +const glsl_type *const glsl_type::uint_type = & builtin_130_types[0]; +const glsl_type *const glsl_type::uvec2_type = & builtin_130_types[1]; +const glsl_type *const glsl_type::uvec3_type = & builtin_130_types[2]; +const glsl_type *const glsl_type::uvec4_type = & builtin_130_types[3]; +/*@}*/ + +/** \name Sampler types added by GL_ARB_texture_rectangle + */ +/*@{*/ + +const glsl_type glsl_type::builtin_ARB_texture_rectangle_types[] = { + glsl_type(GL_SAMPLER_2D_RECT, + GLSL_SAMPLER_DIM_RECT, 0, 0, GLSL_TYPE_FLOAT, "sampler2DRect"), + glsl_type(GL_SAMPLER_2D_RECT_SHADOW, + GLSL_SAMPLER_DIM_RECT, 1, 0, GLSL_TYPE_FLOAT, "sampler2DRectShadow"), +}; +/*@}*/ + +/** \name Sampler types added by GL_EXT_texture_array + */ +/*@{*/ + +const glsl_type glsl_type::builtin_EXT_texture_array_types[] = { + glsl_type(GL_SAMPLER_1D_ARRAY, + GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_FLOAT, "sampler1DArray"), + glsl_type(GL_SAMPLER_2D_ARRAY, + GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_FLOAT, "sampler2DArray"), + glsl_type(GL_SAMPLER_1D_ARRAY_SHADOW, + GLSL_SAMPLER_DIM_1D, 1, 1, GLSL_TYPE_FLOAT, "sampler1DArrayShadow"), + glsl_type(GL_SAMPLER_2D_ARRAY_SHADOW, + GLSL_SAMPLER_DIM_2D, 1, 1, GLSL_TYPE_FLOAT, "sampler2DArrayShadow"), +}; +/*@}*/ + +/** \name Sampler types added by GL_EXT_texture_buffer_object + */ +/*@{*/ + +const glsl_type glsl_type::builtin_EXT_texture_buffer_object_types[] = { + glsl_type(GL_SAMPLER_BUFFER, + GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_FLOAT, "samplerBuffer"), + glsl_type(GL_INT_SAMPLER_BUFFER, + GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_INT, "isamplerBuffer"), + glsl_type(GL_UNSIGNED_INT_SAMPLER_BUFFER, + GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_UINT, "usamplerBuffer"), +}; +/*@}*/ diff --git a/mesalib/src/glsl/glcpp/glcpp-parse.y b/mesalib/src/glsl/glcpp/glcpp-parse.y index bfbabb320..940830416 100644 --- a/mesalib/src/glsl/glcpp/glcpp-parse.y +++ b/mesalib/src/glsl/glcpp/glcpp-parse.y @@ -1,1906 +1,1906 @@ -%{ -/* - * 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 -#include -#include -#include -#include - -#include "glcpp.h" -#include "main/core.h" /* for struct gl_extensions */ -#include "main/mtypes.h" /* for gl_api enum */ - -static void -yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error); - -static void -_define_object_macro (glcpp_parser_t *parser, - YYLTYPE *loc, - const char *macro, - token_list_t *replacements); - -static void -_define_function_macro (glcpp_parser_t *parser, - YYLTYPE *loc, - const char *macro, - string_list_t *parameters, - token_list_t *replacements); - -static string_list_t * -_string_list_create (void *ctx); - -static void -_string_list_append_item (string_list_t *list, const char *str); - -static int -_string_list_contains (string_list_t *list, const char *member, int *index); - -static int -_string_list_length (string_list_t *list); - -static int -_string_list_equal (string_list_t *a, string_list_t *b); - -static argument_list_t * -_argument_list_create (void *ctx); - -static void -_argument_list_append (argument_list_t *list, token_list_t *argument); - -static int -_argument_list_length (argument_list_t *list); - -static token_list_t * -_argument_list_member_at (argument_list_t *list, int index); - -/* Note: This function ralloc_steal()s the str pointer. */ -static token_t * -_token_create_str (void *ctx, int type, char *str); - -static token_t * -_token_create_ival (void *ctx, int type, int ival); - -static token_list_t * -_token_list_create (void *ctx); - -/* Note: This function calls ralloc_steal on token. */ -static void -_token_list_append (token_list_t *list, token_t *token); - -static void -_token_list_append_list (token_list_t *list, token_list_t *tail); - -static int -_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b); - -static void -_parser_active_list_push (glcpp_parser_t *parser, - const char *identifier, - token_node_t *marker); - -static void -_parser_active_list_pop (glcpp_parser_t *parser); - -static int -_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier); - -static void -_glcpp_parser_expand_if (glcpp_parser_t *parser, int type, token_list_t *list); - -static void -_glcpp_parser_expand_token_list (glcpp_parser_t *parser, - token_list_t *list); - -static void -_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, - token_list_t *list); - -static void -_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc, - int condition); - -static void -_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc, - const char *type, int condition); - -static void -_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc); - -#define yylex glcpp_parser_lex - -static int -glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser); - -static void -glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list); - -static void -add_builtin_define(glcpp_parser_t *parser, const char *name, int value); - -%} - -%pure-parser -%error-verbose - -%locations -%initial-action { - @$.first_line = 1; - @$.first_column = 1; - @$.last_line = 1; - @$.last_column = 1; - @$.source = 0; -} - -%parse-param {glcpp_parser_t *parser} -%lex-param {glcpp_parser_t *parser} - -%expect 0 -%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE_FUNC HASH_DEFINE_OBJ HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING NEWLINE OTHER PLACEHOLDER SPACE -%token PASTE -%type expression INTEGER operator SPACE integer_constant -%type IDENTIFIER INTEGER_STRING OTHER -%type identifier_list -%type preprocessing_token conditional_token -%type pp_tokens replacement_list text_line conditional_tokens -%left OR -%left AND -%left '|' -%left '^' -%left '&' -%left EQUAL NOT_EQUAL -%left '<' '>' LESS_OR_EQUAL GREATER_OR_EQUAL -%left LEFT_SHIFT RIGHT_SHIFT -%left '+' '-' -%left '*' '/' '%' -%right UNARY - -%% - -input: - /* empty */ -| input line -; - -line: - control_line { - ralloc_strcat (&parser->output, "\n"); - } -| text_line { - _glcpp_parser_print_expanded_token_list (parser, $1); - ralloc_strcat (&parser->output, "\n"); - ralloc_free ($1); - } -| expanded_line -| HASH non_directive -; - -expanded_line: - IF_EXPANDED expression NEWLINE { - _glcpp_parser_skip_stack_push_if (parser, & @1, $2); - } -| ELIF_EXPANDED expression NEWLINE { - _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2); - } -; - -control_line: - HASH_DEFINE_OBJ IDENTIFIER replacement_list NEWLINE { - _define_object_macro (parser, & @2, $2, $3); - } -| HASH_DEFINE_FUNC IDENTIFIER '(' ')' replacement_list NEWLINE { - _define_function_macro (parser, & @2, $2, NULL, $5); - } -| HASH_DEFINE_FUNC IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE { - _define_function_macro (parser, & @2, $2, $4, $6); - } -| HASH_UNDEF IDENTIFIER NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); - if (macro) { - hash_table_remove (parser->defines, $2); - ralloc_free (macro); - } - ralloc_free ($2); - } -| HASH_IF conditional_tokens NEWLINE { - /* Be careful to only evaluate the 'if' expression if - * we are not skipping. When we are skipping, we - * simply push a new 0-valued 'if' onto the skip - * stack. - * - * This avoids generating diagnostics for invalid - * expressions that are being skipped. */ - if (parser->skip_stack == NULL || - parser->skip_stack->type == SKIP_NO_SKIP) - { - _glcpp_parser_expand_if (parser, IF_EXPANDED, $2); - } - else - { - _glcpp_parser_skip_stack_push_if (parser, & @1, 0); - parser->skip_stack->type = SKIP_TO_ENDIF; - } - } -| HASH_IF NEWLINE { - /* #if without an expression is only an error if we - * are not skipping */ - if (parser->skip_stack == NULL || - parser->skip_stack->type == SKIP_NO_SKIP) - { - glcpp_error(& @1, parser, "#if with no expression"); - } - _glcpp_parser_skip_stack_push_if (parser, & @1, 0); - } -| HASH_IFDEF IDENTIFIER junk NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); - ralloc_free ($2); - _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL); - } -| HASH_IFNDEF IDENTIFIER junk NEWLINE { - macro_t *macro = hash_table_find (parser->defines, $2); - ralloc_free ($2); - _glcpp_parser_skip_stack_push_if (parser, & @1, macro == NULL); - } -| HASH_ELIF conditional_tokens NEWLINE { - /* Be careful to only evaluate the 'elif' expression - * if we are not skipping. When we are skipping, we - * simply change to a 0-valued 'elif' on the skip - * stack. - * - * This avoids generating diagnostics for invalid - * expressions that are being skipped. */ - if (parser->skip_stack && - parser->skip_stack->type == SKIP_TO_ELSE) - { - _glcpp_parser_expand_if (parser, ELIF_EXPANDED, $2); - } - else - { - _glcpp_parser_skip_stack_change_if (parser, & @1, - "elif", 0); - } - } -| HASH_ELIF NEWLINE { - /* #elif without an expression is an error unless we - * are skipping. */ - if (parser->skip_stack && - parser->skip_stack->type == SKIP_TO_ELSE) - { - glcpp_error(& @1, parser, "#elif with no expression"); - } - else - { - _glcpp_parser_skip_stack_change_if (parser, & @1, - "elif", 0); - glcpp_warning(& @1, parser, "ignoring illegal #elif without expression"); - } - } -| HASH_ELSE NEWLINE { - _glcpp_parser_skip_stack_change_if (parser, & @1, "else", 1); - } -| HASH_ENDIF NEWLINE { - _glcpp_parser_skip_stack_pop (parser, & @1); - } -| HASH_VERSION integer_constant NEWLINE { - macro_t *macro = hash_table_find (parser->defines, "__VERSION__"); - if (macro) { - hash_table_remove (parser->defines, "__VERSION__"); - ralloc_free (macro); - } - add_builtin_define (parser, "__VERSION__", $2); - - if ($2 == 100) - add_builtin_define (parser, "GL_ES", 1); - - /* Currently, all ES2 implementations support highp in the - * fragment shader, so we always define this macro in ES2. - * If we ever get a driver that doesn't support highp, we'll - * need to add a flag to the gl_context and check that here. - */ - if ($2 >= 130 || $2 == 100) - add_builtin_define (parser, "GL_FRAGMENT_PRECISION_HIGH", 1); - - ralloc_asprintf_append (&parser->output, "#version %" PRIiMAX, $2); - } -| HASH NEWLINE -; - -integer_constant: - INTEGER_STRING { - if (strlen ($1) >= 3 && strncmp ($1, "0x", 2) == 0) { - $$ = strtoll ($1 + 2, NULL, 16); - } else if ($1[0] == '0') { - $$ = strtoll ($1, NULL, 8); - } else { - $$ = strtoll ($1, NULL, 10); - } - } -| INTEGER { - $$ = $1; - } - -expression: - integer_constant -| expression OR expression { - $$ = $1 || $3; - } -| expression AND expression { - $$ = $1 && $3; - } -| expression '|' expression { - $$ = $1 | $3; - } -| expression '^' expression { - $$ = $1 ^ $3; - } -| expression '&' expression { - $$ = $1 & $3; - } -| expression NOT_EQUAL expression { - $$ = $1 != $3; - } -| expression EQUAL expression { - $$ = $1 == $3; - } -| expression GREATER_OR_EQUAL expression { - $$ = $1 >= $3; - } -| expression LESS_OR_EQUAL expression { - $$ = $1 <= $3; - } -| expression '>' expression { - $$ = $1 > $3; - } -| expression '<' expression { - $$ = $1 < $3; - } -| expression RIGHT_SHIFT expression { - $$ = $1 >> $3; - } -| expression LEFT_SHIFT expression { - $$ = $1 << $3; - } -| expression '-' expression { - $$ = $1 - $3; - } -| expression '+' expression { - $$ = $1 + $3; - } -| expression '%' expression { - if ($3 == 0) { - yyerror (& @1, parser, - "zero modulus in preprocessor directive"); - } else { - $$ = $1 % $3; - } - } -| expression '/' expression { - if ($3 == 0) { - yyerror (& @1, parser, - "division by 0 in preprocessor directive"); - } else { - $$ = $1 / $3; - } - } -| expression '*' expression { - $$ = $1 * $3; - } -| '!' expression %prec UNARY { - $$ = ! $2; - } -| '~' expression %prec UNARY { - $$ = ~ $2; - } -| '-' expression %prec UNARY { - $$ = - $2; - } -| '+' expression %prec UNARY { - $$ = + $2; - } -| '(' expression ')' { - $$ = $2; - } -; - -identifier_list: - IDENTIFIER { - $$ = _string_list_create (parser); - _string_list_append_item ($$, $1); - ralloc_steal ($$, $1); - } -| identifier_list ',' IDENTIFIER { - $$ = $1; - _string_list_append_item ($$, $3); - ralloc_steal ($$, $3); - } -; - -text_line: - NEWLINE { $$ = NULL; } -| pp_tokens NEWLINE -; - -non_directive: - pp_tokens NEWLINE { - yyerror (& @1, parser, "Invalid tokens after #"); - } -; - -replacement_list: - /* empty */ { $$ = NULL; } -| pp_tokens -; - -junk: - /* empty */ -| pp_tokens { - glcpp_warning(&@1, parser, "extra tokens at end of directive"); - } -; - -conditional_token: - /* Handle "defined" operator */ - DEFINED IDENTIFIER { - int v = hash_table_find (parser->defines, $2) ? 1 : 0; - $$ = _token_create_ival (parser, INTEGER, v); - } -| DEFINED '(' IDENTIFIER ')' { - int v = hash_table_find (parser->defines, $3) ? 1 : 0; - $$ = _token_create_ival (parser, INTEGER, v); - } -| preprocessing_token -; - -conditional_tokens: - /* Exactly the same as pp_tokens, but using conditional_token */ - conditional_token { - $$ = _token_list_create (parser); - _token_list_append ($$, $1); - } -| conditional_tokens conditional_token { - $$ = $1; - _token_list_append ($$, $2); - } -; - -pp_tokens: - preprocessing_token { - parser->space_tokens = 1; - $$ = _token_list_create (parser); - _token_list_append ($$, $1); - } -| pp_tokens preprocessing_token { - $$ = $1; - _token_list_append ($$, $2); - } -; - -preprocessing_token: - IDENTIFIER { - $$ = _token_create_str (parser, IDENTIFIER, $1); - $$->location = yylloc; - } -| INTEGER_STRING { - $$ = _token_create_str (parser, INTEGER_STRING, $1); - $$->location = yylloc; - } -| operator { - $$ = _token_create_ival (parser, $1, $1); - $$->location = yylloc; - } -| OTHER { - $$ = _token_create_str (parser, OTHER, $1); - $$->location = yylloc; - } -| SPACE { - $$ = _token_create_ival (parser, SPACE, SPACE); - $$->location = yylloc; - } -; - -operator: - '[' { $$ = '['; } -| ']' { $$ = ']'; } -| '(' { $$ = '('; } -| ')' { $$ = ')'; } -| '{' { $$ = '{'; } -| '}' { $$ = '}'; } -| '.' { $$ = '.'; } -| '&' { $$ = '&'; } -| '*' { $$ = '*'; } -| '+' { $$ = '+'; } -| '-' { $$ = '-'; } -| '~' { $$ = '~'; } -| '!' { $$ = '!'; } -| '/' { $$ = '/'; } -| '%' { $$ = '%'; } -| LEFT_SHIFT { $$ = LEFT_SHIFT; } -| RIGHT_SHIFT { $$ = RIGHT_SHIFT; } -| '<' { $$ = '<'; } -| '>' { $$ = '>'; } -| LESS_OR_EQUAL { $$ = LESS_OR_EQUAL; } -| GREATER_OR_EQUAL { $$ = GREATER_OR_EQUAL; } -| EQUAL { $$ = EQUAL; } -| NOT_EQUAL { $$ = NOT_EQUAL; } -| '^' { $$ = '^'; } -| '|' { $$ = '|'; } -| AND { $$ = AND; } -| OR { $$ = OR; } -| ';' { $$ = ';'; } -| ',' { $$ = ','; } -| '=' { $$ = '='; } -| PASTE { $$ = PASTE; } -; - -%% - -string_list_t * -_string_list_create (void *ctx) -{ - string_list_t *list; - - list = ralloc (ctx, string_list_t); - list->head = NULL; - list->tail = NULL; - - return list; -} - -void -_string_list_append_item (string_list_t *list, const char *str) -{ - string_node_t *node; - - node = ralloc (list, string_node_t); - node->str = ralloc_strdup (node, str); - - node->next = NULL; - - if (list->head == NULL) { - list->head = node; - } else { - list->tail->next = node; - } - - list->tail = node; -} - -int -_string_list_contains (string_list_t *list, const char *member, int *index) -{ - string_node_t *node; - int i; - - if (list == NULL) - return 0; - - for (i = 0, node = list->head; node; i++, node = node->next) { - if (strcmp (node->str, member) == 0) { - if (index) - *index = i; - return 1; - } - } - - return 0; -} - -int -_string_list_length (string_list_t *list) -{ - int length = 0; - string_node_t *node; - - if (list == NULL) - return 0; - - for (node = list->head; node; node = node->next) - length++; - - return length; -} - -int -_string_list_equal (string_list_t *a, string_list_t *b) -{ - string_node_t *node_a, *node_b; - - if (a == NULL && b == NULL) - return 1; - - if (a == NULL || b == NULL) - return 0; - - for (node_a = a->head, node_b = b->head; - node_a && node_b; - node_a = node_a->next, node_b = node_b->next) - { - if (strcmp (node_a->str, node_b->str)) - return 0; - } - - /* Catch the case of lists being different lengths, (which - * would cause the loop above to terminate after the shorter - * list). */ - return node_a == node_b; -} - -argument_list_t * -_argument_list_create (void *ctx) -{ - argument_list_t *list; - - list = ralloc (ctx, argument_list_t); - list->head = NULL; - list->tail = NULL; - - return list; -} - -void -_argument_list_append (argument_list_t *list, token_list_t *argument) -{ - argument_node_t *node; - - node = ralloc (list, argument_node_t); - node->argument = argument; - - node->next = NULL; - - if (list->head == NULL) { - list->head = node; - } else { - list->tail->next = node; - } - - list->tail = node; -} - -int -_argument_list_length (argument_list_t *list) -{ - int length = 0; - argument_node_t *node; - - if (list == NULL) - return 0; - - for (node = list->head; node; node = node->next) - length++; - - return length; -} - -token_list_t * -_argument_list_member_at (argument_list_t *list, int index) -{ - argument_node_t *node; - int i; - - if (list == NULL) - return NULL; - - node = list->head; - for (i = 0; i < index; i++) { - node = node->next; - if (node == NULL) - break; - } - - if (node) - return node->argument; - - return NULL; -} - -/* Note: This function ralloc_steal()s the str pointer. */ -token_t * -_token_create_str (void *ctx, int type, char *str) -{ - token_t *token; - - token = ralloc (ctx, token_t); - token->type = type; - token->value.str = str; - - ralloc_steal (token, str); - - return token; -} - -token_t * -_token_create_ival (void *ctx, int type, int ival) -{ - token_t *token; - - token = ralloc (ctx, token_t); - token->type = type; - token->value.ival = ival; - - return token; -} - -token_list_t * -_token_list_create (void *ctx) -{ - token_list_t *list; - - list = ralloc (ctx, token_list_t); - list->head = NULL; - list->tail = NULL; - list->non_space_tail = NULL; - - return list; -} - -void -_token_list_append (token_list_t *list, token_t *token) -{ - token_node_t *node; - - node = ralloc (list, token_node_t); - node->token = token; - node->next = NULL; - - ralloc_steal (list, token); - - if (list->head == NULL) { - list->head = node; - } else { - list->tail->next = node; - } - - list->tail = node; - if (token->type != SPACE) - list->non_space_tail = node; -} - -void -_token_list_append_list (token_list_t *list, token_list_t *tail) -{ - if (tail == NULL || tail->head == NULL) - return; - - if (list->head == NULL) { - list->head = tail->head; - } else { - list->tail->next = tail->head; - } - - list->tail = tail->tail; - list->non_space_tail = tail->non_space_tail; -} - -static token_list_t * -_token_list_copy (void *ctx, token_list_t *other) -{ - token_list_t *copy; - token_node_t *node; - - if (other == NULL) - return NULL; - - copy = _token_list_create (ctx); - for (node = other->head; node; node = node->next) { - token_t *new_token = ralloc (copy, token_t); - *new_token = *node->token; - _token_list_append (copy, new_token); - } - - return copy; -} - -static void -_token_list_trim_trailing_space (token_list_t *list) -{ - token_node_t *tail, *next; - - if (list->non_space_tail) { - tail = list->non_space_tail->next; - list->non_space_tail->next = NULL; - list->tail = list->non_space_tail; - - while (tail) { - next = tail->next; - ralloc_free (tail); - tail = next; - } - } -} - -static int -_token_list_is_empty_ignoring_space (token_list_t *l) -{ - token_node_t *n; - - if (l == NULL) - return 1; - - n = l->head; - while (n != NULL && n->token->type == SPACE) - n = n->next; - - return n == NULL; -} - -int -_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b) -{ - token_node_t *node_a, *node_b; - - if (a == NULL || b == NULL) { - int a_empty = _token_list_is_empty_ignoring_space(a); - int b_empty = _token_list_is_empty_ignoring_space(b); - return a_empty == b_empty; - } - - node_a = a->head; - node_b = b->head; - - while (1) - { - if (node_a == NULL && node_b == NULL) - break; - - if (node_a == NULL || node_b == NULL) - return 0; - - if (node_a->token->type == SPACE) { - node_a = node_a->next; - continue; - } - - if (node_b->token->type == SPACE) { - node_b = node_b->next; - continue; - } - - if (node_a->token->type != node_b->token->type) - return 0; - - switch (node_a->token->type) { - case INTEGER: - if (node_a->token->value.ival != - node_b->token->value.ival) - { - return 0; - } - break; - case IDENTIFIER: - case INTEGER_STRING: - case OTHER: - if (strcmp (node_a->token->value.str, - node_b->token->value.str)) - { - return 0; - } - break; - } - - node_a = node_a->next; - node_b = node_b->next; - } - - return 1; -} - -static void -_token_print (char **out, token_t *token) -{ - if (token->type < 256) { - ralloc_asprintf_append (out, "%c", token->type); - return; - } - - switch (token->type) { - case INTEGER: - ralloc_asprintf_append (out, "%" PRIiMAX, token->value.ival); - break; - case IDENTIFIER: - case INTEGER_STRING: - case OTHER: - ralloc_strcat (out, token->value.str); - break; - case SPACE: - ralloc_strcat (out, " "); - break; - case LEFT_SHIFT: - ralloc_strcat (out, "<<"); - break; - case RIGHT_SHIFT: - ralloc_strcat (out, ">>"); - break; - case LESS_OR_EQUAL: - ralloc_strcat (out, "<="); - break; - case GREATER_OR_EQUAL: - ralloc_strcat (out, ">="); - break; - case EQUAL: - ralloc_strcat (out, "=="); - break; - case NOT_EQUAL: - ralloc_strcat (out, "!="); - break; - case AND: - ralloc_strcat (out, "&&"); - break; - case OR: - ralloc_strcat (out, "||"); - break; - case PASTE: - ralloc_strcat (out, "##"); - break; - case COMMA_FINAL: - ralloc_strcat (out, ","); - break; - case PLACEHOLDER: - /* Nothing to print. */ - break; - default: - assert(!"Error: Don't know how to print token."); - break; - } -} - -/* Return a new token (ralloc()ed off of 'token') formed by pasting - * 'token' and 'other'. Note that this function may return 'token' or - * 'other' directly rather than allocating anything new. - * - * Caution: Only very cursory error-checking is performed to see if - * the final result is a valid single token. */ -static token_t * -_token_paste (glcpp_parser_t *parser, token_t *token, token_t *other) -{ - token_t *combined = NULL; - - /* Pasting a placeholder onto anything makes no change. */ - if (other->type == PLACEHOLDER) - return token; - - /* When 'token' is a placeholder, just return 'other'. */ - if (token->type == PLACEHOLDER) - return other; - - /* A very few single-character punctuators can be combined - * with another to form a multi-character punctuator. */ - switch (token->type) { - case '<': - if (other->type == '<') - combined = _token_create_ival (token, LEFT_SHIFT, LEFT_SHIFT); - else if (other->type == '=') - combined = _token_create_ival (token, LESS_OR_EQUAL, LESS_OR_EQUAL); - break; - case '>': - if (other->type == '>') - combined = _token_create_ival (token, RIGHT_SHIFT, RIGHT_SHIFT); - else if (other->type == '=') - combined = _token_create_ival (token, GREATER_OR_EQUAL, GREATER_OR_EQUAL); - break; - case '=': - if (other->type == '=') - combined = _token_create_ival (token, EQUAL, EQUAL); - break; - case '!': - if (other->type == '=') - combined = _token_create_ival (token, NOT_EQUAL, NOT_EQUAL); - break; - case '&': - if (other->type == '&') - combined = _token_create_ival (token, AND, AND); - break; - case '|': - if (other->type == '|') - combined = _token_create_ival (token, OR, OR); - break; - } - - if (combined != NULL) { - /* Inherit the location from the first token */ - combined->location = token->location; - return combined; - } - - /* Two string-valued tokens can usually just be mashed - * together. - * - * XXX: This isn't actually legitimate. Several things here - * should result in a diagnostic since the result cannot be a - * valid, single pre-processing token. For example, pasting - * "123" and "abc" is not legal, but we don't catch that - * here. */ - if ((token->type == IDENTIFIER || token->type == OTHER || token->type == INTEGER_STRING) && - (other->type == IDENTIFIER || other->type == OTHER || other->type == INTEGER_STRING)) - { - char *str; - - str = ralloc_asprintf (token, "%s%s", token->value.str, - other->value.str); - combined = _token_create_str (token, token->type, str); - combined->location = token->location; - return combined; - } - - glcpp_error (&token->location, parser, ""); - ralloc_strcat (&parser->info_log, "Pasting \""); - _token_print (&parser->info_log, token); - ralloc_strcat (&parser->info_log, "\" and \""); - _token_print (&parser->info_log, other); - ralloc_strcat (&parser->info_log, "\" does not give a valid preprocessing token.\n"); - - return token; -} - -static void -_token_list_print (glcpp_parser_t *parser, token_list_t *list) -{ - token_node_t *node; - - if (list == NULL) - return; - - for (node = list->head; node; node = node->next) - _token_print (&parser->output, node->token); -} - -void -yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error) -{ - glcpp_error(locp, parser, "%s", error); -} - -static void add_builtin_define(glcpp_parser_t *parser, - const char *name, int value) -{ - token_t *tok; - token_list_t *list; - - tok = _token_create_ival (parser, INTEGER, value); - - list = _token_list_create(parser); - _token_list_append(list, tok); - _define_object_macro(parser, NULL, name, list); -} - -glcpp_parser_t * -glcpp_parser_create (const struct gl_extensions *extensions, int api) -{ - glcpp_parser_t *parser; - int language_version; - - parser = ralloc (NULL, glcpp_parser_t); - - glcpp_lex_init_extra (parser, &parser->scanner); - parser->defines = hash_table_ctor (32, hash_table_string_hash, - hash_table_string_compare); - parser->active = NULL; - parser->lexing_if = 0; - parser->space_tokens = 1; - parser->newline_as_space = 0; - parser->in_control_line = 0; - parser->paren_count = 0; - - parser->skip_stack = NULL; - - parser->lex_from_list = NULL; - parser->lex_from_node = NULL; - - parser->output = ralloc_strdup(parser, ""); - parser->info_log = ralloc_strdup(parser, ""); - parser->error = 0; - - /* Add pre-defined macros. */ - add_builtin_define(parser, "GL_ARB_draw_buffers", 1); - add_builtin_define(parser, "GL_ARB_texture_rectangle", 1); - - if (api == API_OPENGLES2) - add_builtin_define(parser, "GL_ES", 1); - - if (extensions != NULL) { - if (extensions->EXT_texture_array) { - add_builtin_define(parser, "GL_EXT_texture_array", 1); - } - - if (extensions->ARB_fragment_coord_conventions) - add_builtin_define(parser, "GL_ARB_fragment_coord_conventions", - 1); - - if (extensions->ARB_explicit_attrib_location) - add_builtin_define(parser, "GL_ARB_explicit_attrib_location", 1); - - if (extensions->ARB_shader_texture_lod) - add_builtin_define(parser, "GL_ARB_shader_texture_lod", 1); - - if (extensions->AMD_conservative_depth) { - add_builtin_define(parser, "GL_AMD_conservative_depth", 1); - add_builtin_define(parser, "GL_ARB_conservative_depth", 1); - } - } - - language_version = 110; - add_builtin_define(parser, "__VERSION__", language_version); - - return parser; -} - -int -glcpp_parser_parse (glcpp_parser_t *parser) -{ - return yyparse (parser); -} - -void -glcpp_parser_destroy (glcpp_parser_t *parser) -{ - glcpp_lex_destroy (parser->scanner); - hash_table_dtor (parser->defines); - ralloc_free (parser); -} - -typedef enum function_status -{ - FUNCTION_STATUS_SUCCESS, - FUNCTION_NOT_A_FUNCTION, - FUNCTION_UNBALANCED_PARENTHESES -} function_status_t; - -/* Find a set of function-like macro arguments by looking for a - * balanced set of parentheses. - * - * When called, 'node' should be the opening-parenthesis token, (or - * perhaps preceeding SPACE tokens). Upon successful return *last will - * be the last consumed node, (corresponding to the closing right - * parenthesis). - * - * Return values: - * - * FUNCTION_STATUS_SUCCESS: - * - * Successfully parsed a set of function arguments. - * - * FUNCTION_NOT_A_FUNCTION: - * - * Macro name not followed by a '('. This is not an error, but - * simply that the macro name should be treated as a non-macro. - * - * FUNCTION_UNBALANCED_PARENTHESES - * - * Macro name is not followed by a balanced set of parentheses. - */ -static function_status_t -_arguments_parse (argument_list_t *arguments, - token_node_t *node, - token_node_t **last) -{ - token_list_t *argument; - int paren_count; - - node = node->next; - - /* Ignore whitespace before first parenthesis. */ - while (node && node->token->type == SPACE) - node = node->next; - - if (node == NULL || node->token->type != '(') - return FUNCTION_NOT_A_FUNCTION; - - node = node->next; - - argument = _token_list_create (arguments); - _argument_list_append (arguments, argument); - - for (paren_count = 1; node; node = node->next) { - if (node->token->type == '(') - { - paren_count++; - } - else if (node->token->type == ')') - { - paren_count--; - if (paren_count == 0) - break; - } - - if (node->token->type == ',' && - paren_count == 1) - { - _token_list_trim_trailing_space (argument); - argument = _token_list_create (arguments); - _argument_list_append (arguments, argument); - } - else { - if (argument->head == NULL) { - /* Don't treat initial whitespace as - * part of the arguement. */ - if (node->token->type == SPACE) - continue; - } - _token_list_append (argument, node->token); - } - } - - if (paren_count) - return FUNCTION_UNBALANCED_PARENTHESES; - - *last = node; - - return FUNCTION_STATUS_SUCCESS; -} - -static token_list_t * -_token_list_create_with_one_space (void *ctx) -{ - token_list_t *list; - token_t *space; - - list = _token_list_create (ctx); - space = _token_create_ival (list, SPACE, SPACE); - _token_list_append (list, space); - - return list; -} - -static void -_glcpp_parser_expand_if (glcpp_parser_t *parser, int type, token_list_t *list) -{ - token_list_t *expanded; - token_t *token; - - expanded = _token_list_create (parser); - token = _token_create_ival (parser, type, type); - _token_list_append (expanded, token); - _glcpp_parser_expand_token_list (parser, list); - _token_list_append_list (expanded, list); - glcpp_parser_lex_from (parser, expanded); -} - -/* This is a helper function that's essentially part of the - * implementation of _glcpp_parser_expand_node. It shouldn't be called - * except for by that function. - * - * Returns NULL if node is a simple token with no expansion, (that is, - * although 'node' corresponds to an identifier defined as a - * function-like macro, it is not followed with a parenthesized - * argument list). - * - * Compute the complete expansion of node (which is a function-like - * macro) and subsequent nodes which are arguments. - * - * Returns the token list that results from the expansion and sets - * *last to the last node in the list that was consumed by the - * expansion. Specifically, *last will be set as follows: as the - * token of the closing right parenthesis. - */ -static token_list_t * -_glcpp_parser_expand_function (glcpp_parser_t *parser, - token_node_t *node, - token_node_t **last) - -{ - macro_t *macro; - const char *identifier; - argument_list_t *arguments; - function_status_t status; - token_list_t *substituted; - int parameter_index; - - identifier = node->token->value.str; - - macro = hash_table_find (parser->defines, identifier); - - assert (macro->is_function); - - arguments = _argument_list_create (parser); - status = _arguments_parse (arguments, node, last); - - switch (status) { - case FUNCTION_STATUS_SUCCESS: - break; - case FUNCTION_NOT_A_FUNCTION: - return NULL; - case FUNCTION_UNBALANCED_PARENTHESES: - glcpp_error (&node->token->location, parser, "Macro %s call has unbalanced parentheses\n", identifier); - return NULL; - } - - /* Replace a macro defined as empty with a SPACE token. */ - if (macro->replacements == NULL) { - ralloc_free (arguments); - return _token_list_create_with_one_space (parser); - } - - if (! ((_argument_list_length (arguments) == - _string_list_length (macro->parameters)) || - (_string_list_length (macro->parameters) == 0 && - _argument_list_length (arguments) == 1 && - arguments->head->argument->head == NULL))) - { - glcpp_error (&node->token->location, parser, - "Error: macro %s invoked with %d arguments (expected %d)\n", - identifier, - _argument_list_length (arguments), - _string_list_length (macro->parameters)); - return NULL; - } - - /* Perform argument substitution on the replacement list. */ - substituted = _token_list_create (arguments); - - for (node = macro->replacements->head; node; node = node->next) - { - if (node->token->type == IDENTIFIER && - _string_list_contains (macro->parameters, - node->token->value.str, - ¶meter_index)) - { - token_list_t *argument; - argument = _argument_list_member_at (arguments, - parameter_index); - /* Before substituting, we expand the argument - * tokens, or append a placeholder token for - * an empty argument. */ - if (argument->head) { - token_list_t *expanded_argument; - expanded_argument = _token_list_copy (parser, - argument); - _glcpp_parser_expand_token_list (parser, - expanded_argument); - _token_list_append_list (substituted, - expanded_argument); - } else { - token_t *new_token; - - new_token = _token_create_ival (substituted, - PLACEHOLDER, - PLACEHOLDER); - _token_list_append (substituted, new_token); - } - } else { - _token_list_append (substituted, node->token); - } - } - - /* After argument substitution, and before further expansion - * below, implement token pasting. */ - - _token_list_trim_trailing_space (substituted); - - node = substituted->head; - while (node) - { - token_node_t *next_non_space; - - /* Look ahead for a PASTE token, skipping space. */ - next_non_space = node->next; - while (next_non_space && next_non_space->token->type == SPACE) - next_non_space = next_non_space->next; - - if (next_non_space == NULL) - break; - - if (next_non_space->token->type != PASTE) { - node = next_non_space; - continue; - } - - /* Now find the next non-space token after the PASTE. */ - next_non_space = next_non_space->next; - while (next_non_space && next_non_space->token->type == SPACE) - next_non_space = next_non_space->next; - - if (next_non_space == NULL) { - yyerror (&node->token->location, parser, "'##' cannot appear at either end of a macro expansion\n"); - return NULL; - } - - node->token = _token_paste (parser, node->token, next_non_space->token); - node->next = next_non_space->next; - if (next_non_space == substituted->tail) - substituted->tail = node; - - node = node->next; - } - - substituted->non_space_tail = substituted->tail; - - return substituted; -} - -/* Compute the complete expansion of node, (and subsequent nodes after - * 'node' in the case that 'node' is a function-like macro and - * subsequent nodes are arguments). - * - * Returns NULL if node is a simple token with no expansion. - * - * Otherwise, returns the token list that results from the expansion - * and sets *last to the last node in the list that was consumed by - * the expansion. Specifically, *last will be set as follows: - * - * As 'node' in the case of object-like macro expansion. - * - * As the token of the closing right parenthesis in the case of - * function-like macro expansion. - */ -static token_list_t * -_glcpp_parser_expand_node (glcpp_parser_t *parser, - token_node_t *node, - token_node_t **last) -{ - token_t *token = node->token; - const char *identifier; - macro_t *macro; - - /* We only expand identifiers */ - if (token->type != IDENTIFIER) { - /* We change any COMMA into a COMMA_FINAL to prevent - * it being mistaken for an argument separator - * later. */ - if (token->type == ',') { - token->type = COMMA_FINAL; - token->value.ival = COMMA_FINAL; - } - - return NULL; - } - - /* Look up this identifier in the hash table. */ - identifier = token->value.str; - macro = hash_table_find (parser->defines, identifier); - - /* Not a macro, so no expansion needed. */ - if (macro == NULL) - return NULL; - - /* Finally, don't expand this macro if we're already actively - * expanding it, (to avoid infinite recursion). */ - if (_parser_active_list_contains (parser, identifier)) { - /* We change the token type here from IDENTIFIER to - * OTHER to prevent any future expansion of this - * unexpanded token. */ - char *str; - token_list_t *expansion; - token_t *final; - - str = ralloc_strdup (parser, token->value.str); - final = _token_create_str (parser, OTHER, str); - expansion = _token_list_create (parser); - _token_list_append (expansion, final); - *last = node; - return expansion; - } - - if (! macro->is_function) - { - *last = node; - - /* Replace a macro defined as empty with a SPACE token. */ - if (macro->replacements == NULL) - return _token_list_create_with_one_space (parser); - - return _token_list_copy (parser, macro->replacements); - } - - return _glcpp_parser_expand_function (parser, node, last); -} - -/* Push a new identifier onto the parser's active list. - * - * Here, 'marker' is the token node that appears in the list after the - * expansion of 'identifier'. That is, when the list iterator begins - * examining 'marker', then it is time to pop this node from the - * active stack. - */ -static void -_parser_active_list_push (glcpp_parser_t *parser, - const char *identifier, - token_node_t *marker) -{ - active_list_t *node; - - node = ralloc (parser->active, active_list_t); - node->identifier = ralloc_strdup (node, identifier); - node->marker = marker; - node->next = parser->active; - - parser->active = node; -} - -static void -_parser_active_list_pop (glcpp_parser_t *parser) -{ - active_list_t *node = parser->active; - - if (node == NULL) { - parser->active = NULL; - return; - } - - node = parser->active->next; - ralloc_free (parser->active); - - parser->active = node; -} - -static int -_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier) -{ - active_list_t *node; - - if (parser->active == NULL) - return 0; - - for (node = parser->active; node; node = node->next) - if (strcmp (node->identifier, identifier) == 0) - return 1; - - return 0; -} - -/* Walk over the token list replacing nodes with their expansion. - * Whenever nodes are expanded the walking will walk over the new - * nodes, continuing to expand as necessary. The results are placed in - * 'list' itself; - */ -static void -_glcpp_parser_expand_token_list (glcpp_parser_t *parser, - token_list_t *list) -{ - token_node_t *node_prev; - token_node_t *node, *last = NULL; - token_list_t *expansion; - active_list_t *active_initial = parser->active; - - if (list == NULL) - return; - - _token_list_trim_trailing_space (list); - - node_prev = NULL; - node = list->head; - - while (node) { - - while (parser->active && parser->active->marker == node) - _parser_active_list_pop (parser); - - expansion = _glcpp_parser_expand_node (parser, node, &last); - if (expansion) { - token_node_t *n; - - for (n = node; n != last->next; n = n->next) - while (parser->active && - parser->active->marker == n) - { - _parser_active_list_pop (parser); - } - - _parser_active_list_push (parser, - node->token->value.str, - last->next); - - /* Splice expansion into list, supporting a - * simple deletion if the expansion is - * empty. */ - if (expansion->head) { - if (node_prev) - node_prev->next = expansion->head; - else - list->head = expansion->head; - expansion->tail->next = last->next; - if (last == list->tail) - list->tail = expansion->tail; - } else { - if (node_prev) - node_prev->next = last->next; - else - list->head = last->next; - if (last == list->tail) - list->tail = NULL; - } - } else { - node_prev = node; - } - node = node_prev ? node_prev->next : list->head; - } - - /* Remove any lingering effects of this invocation on the - * active list. That is, pop until the list looks like it did - * at the beginning of this function. */ - while (parser->active && parser->active != active_initial) - _parser_active_list_pop (parser); - - list->non_space_tail = list->tail; -} - -void -_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, - token_list_t *list) -{ - if (list == NULL) - return; - - _glcpp_parser_expand_token_list (parser, list); - - _token_list_trim_trailing_space (list); - - _token_list_print (parser, list); -} - -static void -_check_for_reserved_macro_name (glcpp_parser_t *parser, YYLTYPE *loc, - const char *identifier) -{ - /* According to the GLSL specification, macro names starting with "__" - * or "GL_" are reserved for future use. So, don't allow them. - */ - if (strncmp(identifier, "__", 2) == 0) { - glcpp_error (loc, parser, "Macro names starting with \"__\" are reserved.\n"); - } - if (strncmp(identifier, "GL_", 3) == 0) { - glcpp_error (loc, parser, "Macro names starting with \"GL_\" are reserved.\n"); - } -} - -static int -_macro_equal (macro_t *a, macro_t *b) -{ - if (a->is_function != b->is_function) - return 0; - - if (a->is_function) { - if (! _string_list_equal (a->parameters, b->parameters)) - return 0; - } - - return _token_list_equal_ignoring_space (a->replacements, - b->replacements); -} - -void -_define_object_macro (glcpp_parser_t *parser, - YYLTYPE *loc, - const char *identifier, - token_list_t *replacements) -{ - macro_t *macro, *previous; - - if (loc != NULL) - _check_for_reserved_macro_name(parser, loc, identifier); - - macro = ralloc (parser, macro_t); - - macro->is_function = 0; - macro->parameters = NULL; - macro->identifier = ralloc_strdup (macro, identifier); - macro->replacements = replacements; - ralloc_steal (macro, replacements); - - previous = hash_table_find (parser->defines, identifier); - if (previous) { - if (_macro_equal (macro, previous)) { - ralloc_free (macro); - return; - } - glcpp_error (loc, parser, "Redefinition of macro %s\n", - identifier); - } - - hash_table_insert (parser->defines, macro, identifier); -} - -void -_define_function_macro (glcpp_parser_t *parser, - YYLTYPE *loc, - const char *identifier, - string_list_t *parameters, - token_list_t *replacements) -{ - macro_t *macro, *previous; - - _check_for_reserved_macro_name(parser, loc, identifier); - - macro = ralloc (parser, macro_t); - ralloc_steal (macro, parameters); - ralloc_steal (macro, replacements); - - macro->is_function = 1; - macro->parameters = parameters; - macro->identifier = ralloc_strdup (macro, identifier); - macro->replacements = replacements; - previous = hash_table_find (parser->defines, identifier); - if (previous) { - if (_macro_equal (macro, previous)) { - ralloc_free (macro); - return; - } - glcpp_error (loc, parser, "Redefinition of macro %s\n", - identifier); - } - - hash_table_insert (parser->defines, macro, identifier); -} - -static int -glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser) -{ - token_node_t *node; - int ret; - - if (parser->lex_from_list == NULL) { - ret = glcpp_lex (yylval, yylloc, parser->scanner); - - /* XXX: This ugly block of code exists for the sole - * purpose of converting a NEWLINE token into a SPACE - * token, but only in the case where we have seen a - * function-like macro name, but have not yet seen its - * closing parenthesis. - * - * There's perhaps a more compact way to do this with - * mid-rule actions in the grammar. - * - * I'm definitely not pleased with the complexity of - * this code here. - */ - if (parser->newline_as_space) - { - if (ret == '(') { - parser->paren_count++; - } else if (ret == ')') { - parser->paren_count--; - if (parser->paren_count == 0) - parser->newline_as_space = 0; - } else if (ret == NEWLINE) { - ret = SPACE; - } else if (ret != SPACE) { - if (parser->paren_count == 0) - parser->newline_as_space = 0; - } - } - else if (parser->in_control_line) - { - if (ret == NEWLINE) - parser->in_control_line = 0; - } - else if (ret == HASH_DEFINE_OBJ || ret == HASH_DEFINE_FUNC || - ret == HASH_UNDEF || ret == HASH_IF || - ret == HASH_IFDEF || ret == HASH_IFNDEF || - ret == HASH_ELIF || ret == HASH_ELSE || - ret == HASH_ENDIF || ret == HASH) - { - parser->in_control_line = 1; - } - else if (ret == IDENTIFIER) - { - macro_t *macro; - macro = hash_table_find (parser->defines, - yylval->str); - if (macro && macro->is_function) { - parser->newline_as_space = 1; - parser->paren_count = 0; - } - } - - return ret; - } - - node = parser->lex_from_node; - - if (node == NULL) { - ralloc_free (parser->lex_from_list); - parser->lex_from_list = NULL; - return NEWLINE; - } - - *yylval = node->token->value; - ret = node->token->type; - - parser->lex_from_node = node->next; - - return ret; -} - -static void -glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list) -{ - token_node_t *node; - - assert (parser->lex_from_list == NULL); - - /* Copy list, eliminating any space tokens. */ - parser->lex_from_list = _token_list_create (parser); - - for (node = list->head; node; node = node->next) { - if (node->token->type == SPACE) - continue; - _token_list_append (parser->lex_from_list, node->token); - } - - ralloc_free (list); - - parser->lex_from_node = parser->lex_from_list->head; - - /* It's possible the list consisted of nothing but whitespace. */ - if (parser->lex_from_node == NULL) { - ralloc_free (parser->lex_from_list); - parser->lex_from_list = NULL; - } -} - -static void -_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc, - int condition) -{ - skip_type_t current = SKIP_NO_SKIP; - skip_node_t *node; - - if (parser->skip_stack) - current = parser->skip_stack->type; - - node = ralloc (parser, skip_node_t); - node->loc = *loc; - - if (current == SKIP_NO_SKIP) { - if (condition) - node->type = SKIP_NO_SKIP; - else - node->type = SKIP_TO_ELSE; - } else { - node->type = SKIP_TO_ENDIF; - } - - node->next = parser->skip_stack; - parser->skip_stack = node; -} - -static void -_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc, - const char *type, int condition) -{ - if (parser->skip_stack == NULL) { - glcpp_error (loc, parser, "%s without #if\n", type); - return; - } - - if (parser->skip_stack->type == SKIP_TO_ELSE) { - if (condition) - parser->skip_stack->type = SKIP_NO_SKIP; - } else { - parser->skip_stack->type = SKIP_TO_ENDIF; - } -} - -static void -_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc) -{ - skip_node_t *node; - - if (parser->skip_stack == NULL) { - glcpp_error (loc, parser, "#endif without #if\n"); - return; - } - - node = parser->skip_stack; - parser->skip_stack = node->next; - ralloc_free (node); -} +%{ +/* + * 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 +#include +#include +#include +#include + +#include "glcpp.h" +#include "main/core.h" /* for struct gl_extensions */ +#include "main/mtypes.h" /* for gl_api enum */ + +static void +yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error); + +static void +_define_object_macro (glcpp_parser_t *parser, + YYLTYPE *loc, + const char *macro, + token_list_t *replacements); + +static void +_define_function_macro (glcpp_parser_t *parser, + YYLTYPE *loc, + const char *macro, + string_list_t *parameters, + token_list_t *replacements); + +static string_list_t * +_string_list_create (void *ctx); + +static void +_string_list_append_item (string_list_t *list, const char *str); + +static int +_string_list_contains (string_list_t *list, const char *member, int *index); + +static int +_string_list_length (string_list_t *list); + +static int +_string_list_equal (string_list_t *a, string_list_t *b); + +static argument_list_t * +_argument_list_create (void *ctx); + +static void +_argument_list_append (argument_list_t *list, token_list_t *argument); + +static int +_argument_list_length (argument_list_t *list); + +static token_list_t * +_argument_list_member_at (argument_list_t *list, int index); + +/* Note: This function ralloc_steal()s the str pointer. */ +static token_t * +_token_create_str (void *ctx, int type, char *str); + +static token_t * +_token_create_ival (void *ctx, int type, int ival); + +static token_list_t * +_token_list_create (void *ctx); + +/* Note: This function calls ralloc_steal on token. */ +static void +_token_list_append (token_list_t *list, token_t *token); + +static void +_token_list_append_list (token_list_t *list, token_list_t *tail); + +static int +_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b); + +static void +_parser_active_list_push (glcpp_parser_t *parser, + const char *identifier, + token_node_t *marker); + +static void +_parser_active_list_pop (glcpp_parser_t *parser); + +static int +_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier); + +static void +_glcpp_parser_expand_if (glcpp_parser_t *parser, int type, token_list_t *list); + +static void +_glcpp_parser_expand_token_list (glcpp_parser_t *parser, + token_list_t *list); + +static void +_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, + token_list_t *list); + +static void +_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc, + int condition); + +static void +_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc, + const char *type, int condition); + +static void +_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc); + +#define yylex glcpp_parser_lex + +static int +glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser); + +static void +glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list); + +static void +add_builtin_define(glcpp_parser_t *parser, const char *name, int value); + +%} + +%pure-parser +%error-verbose + +%locations +%initial-action { + @$.first_line = 1; + @$.first_column = 1; + @$.last_line = 1; + @$.last_column = 1; + @$.source = 0; +} + +%parse-param {glcpp_parser_t *parser} +%lex-param {glcpp_parser_t *parser} + +%expect 0 +%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE_FUNC HASH_DEFINE_OBJ HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING NEWLINE OTHER PLACEHOLDER SPACE +%token PASTE +%type expression INTEGER operator SPACE integer_constant +%type IDENTIFIER INTEGER_STRING OTHER +%type identifier_list +%type preprocessing_token conditional_token +%type pp_tokens replacement_list text_line conditional_tokens +%left OR +%left AND +%left '|' +%left '^' +%left '&' +%left EQUAL NOT_EQUAL +%left '<' '>' LESS_OR_EQUAL GREATER_OR_EQUAL +%left LEFT_SHIFT RIGHT_SHIFT +%left '+' '-' +%left '*' '/' '%' +%right UNARY + +%% + +input: + /* empty */ +| input line +; + +line: + control_line { + ralloc_strcat (&parser->output, "\n"); + } +| text_line { + _glcpp_parser_print_expanded_token_list (parser, $1); + ralloc_strcat (&parser->output, "\n"); + ralloc_free ($1); + } +| expanded_line +| HASH non_directive +; + +expanded_line: + IF_EXPANDED expression NEWLINE { + _glcpp_parser_skip_stack_push_if (parser, & @1, $2); + } +| ELIF_EXPANDED expression NEWLINE { + _glcpp_parser_skip_stack_change_if (parser, & @1, "elif", $2); + } +; + +control_line: + HASH_DEFINE_OBJ IDENTIFIER replacement_list NEWLINE { + _define_object_macro (parser, & @2, $2, $3); + } +| HASH_DEFINE_FUNC IDENTIFIER '(' ')' replacement_list NEWLINE { + _define_function_macro (parser, & @2, $2, NULL, $5); + } +| HASH_DEFINE_FUNC IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE { + _define_function_macro (parser, & @2, $2, $4, $6); + } +| HASH_UNDEF IDENTIFIER NEWLINE { + macro_t *macro = hash_table_find (parser->defines, $2); + if (macro) { + hash_table_remove (parser->defines, $2); + ralloc_free (macro); + } + ralloc_free ($2); + } +| HASH_IF conditional_tokens NEWLINE { + /* Be careful to only evaluate the 'if' expression if + * we are not skipping. When we are skipping, we + * simply push a new 0-valued 'if' onto the skip + * stack. + * + * This avoids generating diagnostics for invalid + * expressions that are being skipped. */ + if (parser->skip_stack == NULL || + parser->skip_stack->type == SKIP_NO_SKIP) + { + _glcpp_parser_expand_if (parser, IF_EXPANDED, $2); + } + else + { + _glcpp_parser_skip_stack_push_if (parser, & @1, 0); + parser->skip_stack->type = SKIP_TO_ENDIF; + } + } +| HASH_IF NEWLINE { + /* #if without an expression is only an error if we + * are not skipping */ + if (parser->skip_stack == NULL || + parser->skip_stack->type == SKIP_NO_SKIP) + { + glcpp_error(& @1, parser, "#if with no expression"); + } + _glcpp_parser_skip_stack_push_if (parser, & @1, 0); + } +| HASH_IFDEF IDENTIFIER junk NEWLINE { + macro_t *macro = hash_table_find (parser->defines, $2); + ralloc_free ($2); + _glcpp_parser_skip_stack_push_if (parser, & @1, macro != NULL); + } +| HASH_IFNDEF IDENTIFIER junk NEWLINE { + macro_t *macro = hash_table_find (parser->defines, $2); + ralloc_free ($2); + _glcpp_parser_skip_stack_push_if (parser, & @1, macro == NULL); + } +| HASH_ELIF conditional_tokens NEWLINE { + /* Be careful to only evaluate the 'elif' expression + * if we are not skipping. When we are skipping, we + * simply change to a 0-valued 'elif' on the skip + * stack. + * + * This avoids generating diagnostics for invalid + * expressions that are being skipped. */ + if (parser->skip_stack && + parser->skip_stack->type == SKIP_TO_ELSE) + { + _glcpp_parser_expand_if (parser, ELIF_EXPANDED, $2); + } + else + { + _glcpp_parser_skip_stack_change_if (parser, & @1, + "elif", 0); + } + } +| HASH_ELIF NEWLINE { + /* #elif without an expression is an error unless we + * are skipping. */ + if (parser->skip_stack && + parser->skip_stack->type == SKIP_TO_ELSE) + { + glcpp_error(& @1, parser, "#elif with no expression"); + } + else + { + _glcpp_parser_skip_stack_change_if (parser, & @1, + "elif", 0); + glcpp_warning(& @1, parser, "ignoring illegal #elif without expression"); + } + } +| HASH_ELSE NEWLINE { + _glcpp_parser_skip_stack_change_if (parser, & @1, "else", 1); + } +| HASH_ENDIF NEWLINE { + _glcpp_parser_skip_stack_pop (parser, & @1); + } +| HASH_VERSION integer_constant NEWLINE { + macro_t *macro = hash_table_find (parser->defines, "__VERSION__"); + if (macro) { + hash_table_remove (parser->defines, "__VERSION__"); + ralloc_free (macro); + } + add_builtin_define (parser, "__VERSION__", $2); + + if ($2 == 100) + add_builtin_define (parser, "GL_ES", 1); + + /* Currently, all ES2 implementations support highp in the + * fragment shader, so we always define this macro in ES2. + * If we ever get a driver that doesn't support highp, we'll + * need to add a flag to the gl_context and check that here. + */ + if ($2 >= 130 || $2 == 100) + add_builtin_define (parser, "GL_FRAGMENT_PRECISION_HIGH", 1); + + ralloc_asprintf_append (&parser->output, "#version %" PRIiMAX, $2); + } +| HASH NEWLINE +; + +integer_constant: + INTEGER_STRING { + if (strlen ($1) >= 3 && strncmp ($1, "0x", 2) == 0) { + $$ = strtoll ($1 + 2, NULL, 16); + } else if ($1[0] == '0') { + $$ = strtoll ($1, NULL, 8); + } else { + $$ = strtoll ($1, NULL, 10); + } + } +| INTEGER { + $$ = $1; + } + +expression: + integer_constant +| expression OR expression { + $$ = $1 || $3; + } +| expression AND expression { + $$ = $1 && $3; + } +| expression '|' expression { + $$ = $1 | $3; + } +| expression '^' expression { + $$ = $1 ^ $3; + } +| expression '&' expression { + $$ = $1 & $3; + } +| expression NOT_EQUAL expression { + $$ = $1 != $3; + } +| expression EQUAL expression { + $$ = $1 == $3; + } +| expression GREATER_OR_EQUAL expression { + $$ = $1 >= $3; + } +| expression LESS_OR_EQUAL expression { + $$ = $1 <= $3; + } +| expression '>' expression { + $$ = $1 > $3; + } +| expression '<' expression { + $$ = $1 < $3; + } +| expression RIGHT_SHIFT expression { + $$ = $1 >> $3; + } +| expression LEFT_SHIFT expression { + $$ = $1 << $3; + } +| expression '-' expression { + $$ = $1 - $3; + } +| expression '+' expression { + $$ = $1 + $3; + } +| expression '%' expression { + if ($3 == 0) { + yyerror (& @1, parser, + "zero modulus in preprocessor directive"); + } else { + $$ = $1 % $3; + } + } +| expression '/' expression { + if ($3 == 0) { + yyerror (& @1, parser, + "division by 0 in preprocessor directive"); + } else { + $$ = $1 / $3; + } + } +| expression '*' expression { + $$ = $1 * $3; + } +| '!' expression %prec UNARY { + $$ = ! $2; + } +| '~' expression %prec UNARY { + $$ = ~ $2; + } +| '-' expression %prec UNARY { + $$ = - $2; + } +| '+' expression %prec UNARY { + $$ = + $2; + } +| '(' expression ')' { + $$ = $2; + } +; + +identifier_list: + IDENTIFIER { + $$ = _string_list_create (parser); + _string_list_append_item ($$, $1); + ralloc_steal ($$, $1); + } +| identifier_list ',' IDENTIFIER { + $$ = $1; + _string_list_append_item ($$, $3); + ralloc_steal ($$, $3); + } +; + +text_line: + NEWLINE { $$ = NULL; } +| pp_tokens NEWLINE +; + +non_directive: + pp_tokens NEWLINE { + yyerror (& @1, parser, "Invalid tokens after #"); + } +; + +replacement_list: + /* empty */ { $$ = NULL; } +| pp_tokens +; + +junk: + /* empty */ +| pp_tokens { + glcpp_warning(&@1, parser, "extra tokens at end of directive"); + } +; + +conditional_token: + /* Handle "defined" operator */ + DEFINED IDENTIFIER { + int v = hash_table_find (parser->defines, $2) ? 1 : 0; + $$ = _token_create_ival (parser, INTEGER, v); + } +| DEFINED '(' IDENTIFIER ')' { + int v = hash_table_find (parser->defines, $3) ? 1 : 0; + $$ = _token_create_ival (parser, INTEGER, v); + } +| preprocessing_token +; + +conditional_tokens: + /* Exactly the same as pp_tokens, but using conditional_token */ + conditional_token { + $$ = _token_list_create (parser); + _token_list_append ($$, $1); + } +| conditional_tokens conditional_token { + $$ = $1; + _token_list_append ($$, $2); + } +; + +pp_tokens: + preprocessing_token { + parser->space_tokens = 1; + $$ = _token_list_create (parser); + _token_list_append ($$, $1); + } +| pp_tokens preprocessing_token { + $$ = $1; + _token_list_append ($$, $2); + } +; + +preprocessing_token: + IDENTIFIER { + $$ = _token_create_str (parser, IDENTIFIER, $1); + $$->location = yylloc; + } +| INTEGER_STRING { + $$ = _token_create_str (parser, INTEGER_STRING, $1); + $$->location = yylloc; + } +| operator { + $$ = _token_create_ival (parser, $1, $1); + $$->location = yylloc; + } +| OTHER { + $$ = _token_create_str (parser, OTHER, $1); + $$->location = yylloc; + } +| SPACE { + $$ = _token_create_ival (parser, SPACE, SPACE); + $$->location = yylloc; + } +; + +operator: + '[' { $$ = '['; } +| ']' { $$ = ']'; } +| '(' { $$ = '('; } +| ')' { $$ = ')'; } +| '{' { $$ = '{'; } +| '}' { $$ = '}'; } +| '.' { $$ = '.'; } +| '&' { $$ = '&'; } +| '*' { $$ = '*'; } +| '+' { $$ = '+'; } +| '-' { $$ = '-'; } +| '~' { $$ = '~'; } +| '!' { $$ = '!'; } +| '/' { $$ = '/'; } +| '%' { $$ = '%'; } +| LEFT_SHIFT { $$ = LEFT_SHIFT; } +| RIGHT_SHIFT { $$ = RIGHT_SHIFT; } +| '<' { $$ = '<'; } +| '>' { $$ = '>'; } +| LESS_OR_EQUAL { $$ = LESS_OR_EQUAL; } +| GREATER_OR_EQUAL { $$ = GREATER_OR_EQUAL; } +| EQUAL { $$ = EQUAL; } +| NOT_EQUAL { $$ = NOT_EQUAL; } +| '^' { $$ = '^'; } +| '|' { $$ = '|'; } +| AND { $$ = AND; } +| OR { $$ = OR; } +| ';' { $$ = ';'; } +| ',' { $$ = ','; } +| '=' { $$ = '='; } +| PASTE { $$ = PASTE; } +; + +%% + +string_list_t * +_string_list_create (void *ctx) +{ + string_list_t *list; + + list = ralloc (ctx, string_list_t); + list->head = NULL; + list->tail = NULL; + + return list; +} + +void +_string_list_append_item (string_list_t *list, const char *str) +{ + string_node_t *node; + + node = ralloc (list, string_node_t); + node->str = ralloc_strdup (node, str); + + node->next = NULL; + + if (list->head == NULL) { + list->head = node; + } else { + list->tail->next = node; + } + + list->tail = node; +} + +int +_string_list_contains (string_list_t *list, const char *member, int *index) +{ + string_node_t *node; + int i; + + if (list == NULL) + return 0; + + for (i = 0, node = list->head; node; i++, node = node->next) { + if (strcmp (node->str, member) == 0) { + if (index) + *index = i; + return 1; + } + } + + return 0; +} + +int +_string_list_length (string_list_t *list) +{ + int length = 0; + string_node_t *node; + + if (list == NULL) + return 0; + + for (node = list->head; node; node = node->next) + length++; + + return length; +} + +int +_string_list_equal (string_list_t *a, string_list_t *b) +{ + string_node_t *node_a, *node_b; + + if (a == NULL && b == NULL) + return 1; + + if (a == NULL || b == NULL) + return 0; + + for (node_a = a->head, node_b = b->head; + node_a && node_b; + node_a = node_a->next, node_b = node_b->next) + { + if (strcmp (node_a->str, node_b->str)) + return 0; + } + + /* Catch the case of lists being different lengths, (which + * would cause the loop above to terminate after the shorter + * list). */ + return node_a == node_b; +} + +argument_list_t * +_argument_list_create (void *ctx) +{ + argument_list_t *list; + + list = ralloc (ctx, argument_list_t); + list->head = NULL; + list->tail = NULL; + + return list; +} + +void +_argument_list_append (argument_list_t *list, token_list_t *argument) +{ + argument_node_t *node; + + node = ralloc (list, argument_node_t); + node->argument = argument; + + node->next = NULL; + + if (list->head == NULL) { + list->head = node; + } else { + list->tail->next = node; + } + + list->tail = node; +} + +int +_argument_list_length (argument_list_t *list) +{ + int length = 0; + argument_node_t *node; + + if (list == NULL) + return 0; + + for (node = list->head; node; node = node->next) + length++; + + return length; +} + +token_list_t * +_argument_list_member_at (argument_list_t *list, int index) +{ + argument_node_t *node; + int i; + + if (list == NULL) + return NULL; + + node = list->head; + for (i = 0; i < index; i++) { + node = node->next; + if (node == NULL) + break; + } + + if (node) + return node->argument; + + return NULL; +} + +/* Note: This function ralloc_steal()s the str pointer. */ +token_t * +_token_create_str (void *ctx, int type, char *str) +{ + token_t *token; + + token = ralloc (ctx, token_t); + token->type = type; + token->value.str = str; + + ralloc_steal (token, str); + + return token; +} + +token_t * +_token_create_ival (void *ctx, int type, int ival) +{ + token_t *token; + + token = ralloc (ctx, token_t); + token->type = type; + token->value.ival = ival; + + return token; +} + +token_list_t * +_token_list_create (void *ctx) +{ + token_list_t *list; + + list = ralloc (ctx, token_list_t); + list->head = NULL; + list->tail = NULL; + list->non_space_tail = NULL; + + return list; +} + +void +_token_list_append (token_list_t *list, token_t *token) +{ + token_node_t *node; + + node = ralloc (list, token_node_t); + node->token = token; + node->next = NULL; + + ralloc_steal (list, token); + + if (list->head == NULL) { + list->head = node; + } else { + list->tail->next = node; + } + + list->tail = node; + if (token->type != SPACE) + list->non_space_tail = node; +} + +void +_token_list_append_list (token_list_t *list, token_list_t *tail) +{ + if (tail == NULL || tail->head == NULL) + return; + + if (list->head == NULL) { + list->head = tail->head; + } else { + list->tail->next = tail->head; + } + + list->tail = tail->tail; + list->non_space_tail = tail->non_space_tail; +} + +static token_list_t * +_token_list_copy (void *ctx, token_list_t *other) +{ + token_list_t *copy; + token_node_t *node; + + if (other == NULL) + return NULL; + + copy = _token_list_create (ctx); + for (node = other->head; node; node = node->next) { + token_t *new_token = ralloc (copy, token_t); + *new_token = *node->token; + _token_list_append (copy, new_token); + } + + return copy; +} + +static void +_token_list_trim_trailing_space (token_list_t *list) +{ + token_node_t *tail, *next; + + if (list->non_space_tail) { + tail = list->non_space_tail->next; + list->non_space_tail->next = NULL; + list->tail = list->non_space_tail; + + while (tail) { + next = tail->next; + ralloc_free (tail); + tail = next; + } + } +} + +static int +_token_list_is_empty_ignoring_space (token_list_t *l) +{ + token_node_t *n; + + if (l == NULL) + return 1; + + n = l->head; + while (n != NULL && n->token->type == SPACE) + n = n->next; + + return n == NULL; +} + +int +_token_list_equal_ignoring_space (token_list_t *a, token_list_t *b) +{ + token_node_t *node_a, *node_b; + + if (a == NULL || b == NULL) { + int a_empty = _token_list_is_empty_ignoring_space(a); + int b_empty = _token_list_is_empty_ignoring_space(b); + return a_empty == b_empty; + } + + node_a = a->head; + node_b = b->head; + + while (1) + { + if (node_a == NULL && node_b == NULL) + break; + + if (node_a == NULL || node_b == NULL) + return 0; + + if (node_a->token->type == SPACE) { + node_a = node_a->next; + continue; + } + + if (node_b->token->type == SPACE) { + node_b = node_b->next; + continue; + } + + if (node_a->token->type != node_b->token->type) + return 0; + + switch (node_a->token->type) { + case INTEGER: + if (node_a->token->value.ival != + node_b->token->value.ival) + { + return 0; + } + break; + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: + if (strcmp (node_a->token->value.str, + node_b->token->value.str)) + { + return 0; + } + break; + } + + node_a = node_a->next; + node_b = node_b->next; + } + + return 1; +} + +static void +_token_print (char **out, token_t *token) +{ + if (token->type < 256) { + ralloc_asprintf_append (out, "%c", token->type); + return; + } + + switch (token->type) { + case INTEGER: + ralloc_asprintf_append (out, "%" PRIiMAX, token->value.ival); + break; + case IDENTIFIER: + case INTEGER_STRING: + case OTHER: + ralloc_strcat (out, token->value.str); + break; + case SPACE: + ralloc_strcat (out, " "); + break; + case LEFT_SHIFT: + ralloc_strcat (out, "<<"); + break; + case RIGHT_SHIFT: + ralloc_strcat (out, ">>"); + break; + case LESS_OR_EQUAL: + ralloc_strcat (out, "<="); + break; + case GREATER_OR_EQUAL: + ralloc_strcat (out, ">="); + break; + case EQUAL: + ralloc_strcat (out, "=="); + break; + case NOT_EQUAL: + ralloc_strcat (out, "!="); + break; + case AND: + ralloc_strcat (out, "&&"); + break; + case OR: + ralloc_strcat (out, "||"); + break; + case PASTE: + ralloc_strcat (out, "##"); + break; + case COMMA_FINAL: + ralloc_strcat (out, ","); + break; + case PLACEHOLDER: + /* Nothing to print. */ + break; + default: + assert(!"Error: Don't know how to print token."); + break; + } +} + +/* Return a new token (ralloc()ed off of 'token') formed by pasting + * 'token' and 'other'. Note that this function may return 'token' or + * 'other' directly rather than allocating anything new. + * + * Caution: Only very cursory error-checking is performed to see if + * the final result is a valid single token. */ +static token_t * +_token_paste (glcpp_parser_t *parser, token_t *token, token_t *other) +{ + token_t *combined = NULL; + + /* Pasting a placeholder onto anything makes no change. */ + if (other->type == PLACEHOLDER) + return token; + + /* When 'token' is a placeholder, just return 'other'. */ + if (token->type == PLACEHOLDER) + return other; + + /* A very few single-character punctuators can be combined + * with another to form a multi-character punctuator. */ + switch (token->type) { + case '<': + if (other->type == '<') + combined = _token_create_ival (token, LEFT_SHIFT, LEFT_SHIFT); + else if (other->type == '=') + combined = _token_create_ival (token, LESS_OR_EQUAL, LESS_OR_EQUAL); + break; + case '>': + if (other->type == '>') + combined = _token_create_ival (token, RIGHT_SHIFT, RIGHT_SHIFT); + else if (other->type == '=') + combined = _token_create_ival (token, GREATER_OR_EQUAL, GREATER_OR_EQUAL); + break; + case '=': + if (other->type == '=') + combined = _token_create_ival (token, EQUAL, EQUAL); + break; + case '!': + if (other->type == '=') + combined = _token_create_ival (token, NOT_EQUAL, NOT_EQUAL); + break; + case '&': + if (other->type == '&') + combined = _token_create_ival (token, AND, AND); + break; + case '|': + if (other->type == '|') + combined = _token_create_ival (token, OR, OR); + break; + } + + if (combined != NULL) { + /* Inherit the location from the first token */ + combined->location = token->location; + return combined; + } + + /* Two string-valued tokens can usually just be mashed + * together. + * + * XXX: This isn't actually legitimate. Several things here + * should result in a diagnostic since the result cannot be a + * valid, single pre-processing token. For example, pasting + * "123" and "abc" is not legal, but we don't catch that + * here. */ + if ((token->type == IDENTIFIER || token->type == OTHER || token->type == INTEGER_STRING) && + (other->type == IDENTIFIER || other->type == OTHER || other->type == INTEGER_STRING)) + { + char *str; + + str = ralloc_asprintf (token, "%s%s", token->value.str, + other->value.str); + combined = _token_create_str (token, token->type, str); + combined->location = token->location; + return combined; + } + + glcpp_error (&token->location, parser, ""); + ralloc_strcat (&parser->info_log, "Pasting \""); + _token_print (&parser->info_log, token); + ralloc_strcat (&parser->info_log, "\" and \""); + _token_print (&parser->info_log, other); + ralloc_strcat (&parser->info_log, "\" does not give a valid preprocessing token.\n"); + + return token; +} + +static void +_token_list_print (glcpp_parser_t *parser, token_list_t *list) +{ + token_node_t *node; + + if (list == NULL) + return; + + for (node = list->head; node; node = node->next) + _token_print (&parser->output, node->token); +} + +void +yyerror (YYLTYPE *locp, glcpp_parser_t *parser, const char *error) +{ + glcpp_error(locp, parser, "%s", error); +} + +static void add_builtin_define(glcpp_parser_t *parser, + const char *name, int value) +{ + token_t *tok; + token_list_t *list; + + tok = _token_create_ival (parser, INTEGER, value); + + list = _token_list_create(parser); + _token_list_append(list, tok); + _define_object_macro(parser, NULL, name, list); +} + +glcpp_parser_t * +glcpp_parser_create (const struct gl_extensions *extensions, int api) +{ + glcpp_parser_t *parser; + int language_version; + + parser = ralloc (NULL, glcpp_parser_t); + + glcpp_lex_init_extra (parser, &parser->scanner); + parser->defines = hash_table_ctor (32, hash_table_string_hash, + hash_table_string_compare); + parser->active = NULL; + parser->lexing_if = 0; + parser->space_tokens = 1; + parser->newline_as_space = 0; + parser->in_control_line = 0; + parser->paren_count = 0; + + parser->skip_stack = NULL; + + parser->lex_from_list = NULL; + parser->lex_from_node = NULL; + + parser->output = ralloc_strdup(parser, ""); + parser->info_log = ralloc_strdup(parser, ""); + parser->error = 0; + + /* Add pre-defined macros. */ + add_builtin_define(parser, "GL_ARB_draw_buffers", 1); + add_builtin_define(parser, "GL_ARB_texture_rectangle", 1); + + if (api == API_OPENGLES2) + add_builtin_define(parser, "GL_ES", 1); + + if (extensions != NULL) { + if (extensions->EXT_texture_array) { + add_builtin_define(parser, "GL_EXT_texture_array", 1); + } + + if (extensions->ARB_fragment_coord_conventions) + add_builtin_define(parser, "GL_ARB_fragment_coord_conventions", + 1); + + if (extensions->ARB_explicit_attrib_location) + add_builtin_define(parser, "GL_ARB_explicit_attrib_location", 1); + + if (extensions->ARB_shader_texture_lod) + add_builtin_define(parser, "GL_ARB_shader_texture_lod", 1); + + if (extensions->AMD_conservative_depth) { + add_builtin_define(parser, "GL_AMD_conservative_depth", 1); + add_builtin_define(parser, "GL_ARB_conservative_depth", 1); + } + } + + language_version = 110; + add_builtin_define(parser, "__VERSION__", language_version); + + return parser; +} + +int +glcpp_parser_parse (glcpp_parser_t *parser) +{ + return yyparse (parser); +} + +void +glcpp_parser_destroy (glcpp_parser_t *parser) +{ + glcpp_lex_destroy (parser->scanner); + hash_table_dtor (parser->defines); + ralloc_free (parser); +} + +typedef enum function_status +{ + FUNCTION_STATUS_SUCCESS, + FUNCTION_NOT_A_FUNCTION, + FUNCTION_UNBALANCED_PARENTHESES +} function_status_t; + +/* Find a set of function-like macro arguments by looking for a + * balanced set of parentheses. + * + * When called, 'node' should be the opening-parenthesis token, (or + * perhaps preceeding SPACE tokens). Upon successful return *last will + * be the last consumed node, (corresponding to the closing right + * parenthesis). + * + * Return values: + * + * FUNCTION_STATUS_SUCCESS: + * + * Successfully parsed a set of function arguments. + * + * FUNCTION_NOT_A_FUNCTION: + * + * Macro name not followed by a '('. This is not an error, but + * simply that the macro name should be treated as a non-macro. + * + * FUNCTION_UNBALANCED_PARENTHESES + * + * Macro name is not followed by a balanced set of parentheses. + */ +static function_status_t +_arguments_parse (argument_list_t *arguments, + token_node_t *node, + token_node_t **last) +{ + token_list_t *argument; + int paren_count; + + node = node->next; + + /* Ignore whitespace before first parenthesis. */ + while (node && node->token->type == SPACE) + node = node->next; + + if (node == NULL || node->token->type != '(') + return FUNCTION_NOT_A_FUNCTION; + + node = node->next; + + argument = _token_list_create (arguments); + _argument_list_append (arguments, argument); + + for (paren_count = 1; node; node = node->next) { + if (node->token->type == '(') + { + paren_count++; + } + else if (node->token->type == ')') + { + paren_count--; + if (paren_count == 0) + break; + } + + if (node->token->type == ',' && + paren_count == 1) + { + _token_list_trim_trailing_space (argument); + argument = _token_list_create (arguments); + _argument_list_append (arguments, argument); + } + else { + if (argument->head == NULL) { + /* Don't treat initial whitespace as + * part of the arguement. */ + if (node->token->type == SPACE) + continue; + } + _token_list_append (argument, node->token); + } + } + + if (paren_count) + return FUNCTION_UNBALANCED_PARENTHESES; + + *last = node; + + return FUNCTION_STATUS_SUCCESS; +} + +static token_list_t * +_token_list_create_with_one_space (void *ctx) +{ + token_list_t *list; + token_t *space; + + list = _token_list_create (ctx); + space = _token_create_ival (list, SPACE, SPACE); + _token_list_append (list, space); + + return list; +} + +static void +_glcpp_parser_expand_if (glcpp_parser_t *parser, int type, token_list_t *list) +{ + token_list_t *expanded; + token_t *token; + + expanded = _token_list_create (parser); + token = _token_create_ival (parser, type, type); + _token_list_append (expanded, token); + _glcpp_parser_expand_token_list (parser, list); + _token_list_append_list (expanded, list); + glcpp_parser_lex_from (parser, expanded); +} + +/* This is a helper function that's essentially part of the + * implementation of _glcpp_parser_expand_node. It shouldn't be called + * except for by that function. + * + * Returns NULL if node is a simple token with no expansion, (that is, + * although 'node' corresponds to an identifier defined as a + * function-like macro, it is not followed with a parenthesized + * argument list). + * + * Compute the complete expansion of node (which is a function-like + * macro) and subsequent nodes which are arguments. + * + * Returns the token list that results from the expansion and sets + * *last to the last node in the list that was consumed by the + * expansion. Specifically, *last will be set as follows: as the + * token of the closing right parenthesis. + */ +static token_list_t * +_glcpp_parser_expand_function (glcpp_parser_t *parser, + token_node_t *node, + token_node_t **last) + +{ + macro_t *macro; + const char *identifier; + argument_list_t *arguments; + function_status_t status; + token_list_t *substituted; + int parameter_index; + + identifier = node->token->value.str; + + macro = hash_table_find (parser->defines, identifier); + + assert (macro->is_function); + + arguments = _argument_list_create (parser); + status = _arguments_parse (arguments, node, last); + + switch (status) { + case FUNCTION_STATUS_SUCCESS: + break; + case FUNCTION_NOT_A_FUNCTION: + return NULL; + case FUNCTION_UNBALANCED_PARENTHESES: + glcpp_error (&node->token->location, parser, "Macro %s call has unbalanced parentheses\n", identifier); + return NULL; + } + + /* Replace a macro defined as empty with a SPACE token. */ + if (macro->replacements == NULL) { + ralloc_free (arguments); + return _token_list_create_with_one_space (parser); + } + + if (! ((_argument_list_length (arguments) == + _string_list_length (macro->parameters)) || + (_string_list_length (macro->parameters) == 0 && + _argument_list_length (arguments) == 1 && + arguments->head->argument->head == NULL))) + { + glcpp_error (&node->token->location, parser, + "Error: macro %s invoked with %d arguments (expected %d)\n", + identifier, + _argument_list_length (arguments), + _string_list_length (macro->parameters)); + return NULL; + } + + /* Perform argument substitution on the replacement list. */ + substituted = _token_list_create (arguments); + + for (node = macro->replacements->head; node; node = node->next) + { + if (node->token->type == IDENTIFIER && + _string_list_contains (macro->parameters, + node->token->value.str, + ¶meter_index)) + { + token_list_t *argument; + argument = _argument_list_member_at (arguments, + parameter_index); + /* Before substituting, we expand the argument + * tokens, or append a placeholder token for + * an empty argument. */ + if (argument->head) { + token_list_t *expanded_argument; + expanded_argument = _token_list_copy (parser, + argument); + _glcpp_parser_expand_token_list (parser, + expanded_argument); + _token_list_append_list (substituted, + expanded_argument); + } else { + token_t *new_token; + + new_token = _token_create_ival (substituted, + PLACEHOLDER, + PLACEHOLDER); + _token_list_append (substituted, new_token); + } + } else { + _token_list_append (substituted, node->token); + } + } + + /* After argument substitution, and before further expansion + * below, implement token pasting. */ + + _token_list_trim_trailing_space (substituted); + + node = substituted->head; + while (node) + { + token_node_t *next_non_space; + + /* Look ahead for a PASTE token, skipping space. */ + next_non_space = node->next; + while (next_non_space && next_non_space->token->type == SPACE) + next_non_space = next_non_space->next; + + if (next_non_space == NULL) + break; + + if (next_non_space->token->type != PASTE) { + node = next_non_space; + continue; + } + + /* Now find the next non-space token after the PASTE. */ + next_non_space = next_non_space->next; + while (next_non_space && next_non_space->token->type == SPACE) + next_non_space = next_non_space->next; + + if (next_non_space == NULL) { + yyerror (&node->token->location, parser, "'##' cannot appear at either end of a macro expansion\n"); + return NULL; + } + + node->token = _token_paste (parser, node->token, next_non_space->token); + node->next = next_non_space->next; + if (next_non_space == substituted->tail) + substituted->tail = node; + + node = node->next; + } + + substituted->non_space_tail = substituted->tail; + + return substituted; +} + +/* Compute the complete expansion of node, (and subsequent nodes after + * 'node' in the case that 'node' is a function-like macro and + * subsequent nodes are arguments). + * + * Returns NULL if node is a simple token with no expansion. + * + * Otherwise, returns the token list that results from the expansion + * and sets *last to the last node in the list that was consumed by + * the expansion. Specifically, *last will be set as follows: + * + * As 'node' in the case of object-like macro expansion. + * + * As the token of the closing right parenthesis in the case of + * function-like macro expansion. + */ +static token_list_t * +_glcpp_parser_expand_node (glcpp_parser_t *parser, + token_node_t *node, + token_node_t **last) +{ + token_t *token = node->token; + const char *identifier; + macro_t *macro; + + /* We only expand identifiers */ + if (token->type != IDENTIFIER) { + /* We change any COMMA into a COMMA_FINAL to prevent + * it being mistaken for an argument separator + * later. */ + if (token->type == ',') { + token->type = COMMA_FINAL; + token->value.ival = COMMA_FINAL; + } + + return NULL; + } + + /* Look up this identifier in the hash table. */ + identifier = token->value.str; + macro = hash_table_find (parser->defines, identifier); + + /* Not a macro, so no expansion needed. */ + if (macro == NULL) + return NULL; + + /* Finally, don't expand this macro if we're already actively + * expanding it, (to avoid infinite recursion). */ + if (_parser_active_list_contains (parser, identifier)) { + /* We change the token type here from IDENTIFIER to + * OTHER to prevent any future expansion of this + * unexpanded token. */ + char *str; + token_list_t *expansion; + token_t *final; + + str = ralloc_strdup (parser, token->value.str); + final = _token_create_str (parser, OTHER, str); + expansion = _token_list_create (parser); + _token_list_append (expansion, final); + *last = node; + return expansion; + } + + if (! macro->is_function) + { + *last = node; + + /* Replace a macro defined as empty with a SPACE token. */ + if (macro->replacements == NULL) + return _token_list_create_with_one_space (parser); + + return _token_list_copy (parser, macro->replacements); + } + + return _glcpp_parser_expand_function (parser, node, last); +} + +/* Push a new identifier onto the parser's active list. + * + * Here, 'marker' is the token node that appears in the list after the + * expansion of 'identifier'. That is, when the list iterator begins + * examining 'marker', then it is time to pop this node from the + * active stack. + */ +static void +_parser_active_list_push (glcpp_parser_t *parser, + const char *identifier, + token_node_t *marker) +{ + active_list_t *node; + + node = ralloc (parser->active, active_list_t); + node->identifier = ralloc_strdup (node, identifier); + node->marker = marker; + node->next = parser->active; + + parser->active = node; +} + +static void +_parser_active_list_pop (glcpp_parser_t *parser) +{ + active_list_t *node = parser->active; + + if (node == NULL) { + parser->active = NULL; + return; + } + + node = parser->active->next; + ralloc_free (parser->active); + + parser->active = node; +} + +static int +_parser_active_list_contains (glcpp_parser_t *parser, const char *identifier) +{ + active_list_t *node; + + if (parser->active == NULL) + return 0; + + for (node = parser->active; node; node = node->next) + if (strcmp (node->identifier, identifier) == 0) + return 1; + + return 0; +} + +/* Walk over the token list replacing nodes with their expansion. + * Whenever nodes are expanded the walking will walk over the new + * nodes, continuing to expand as necessary. The results are placed in + * 'list' itself; + */ +static void +_glcpp_parser_expand_token_list (glcpp_parser_t *parser, + token_list_t *list) +{ + token_node_t *node_prev; + token_node_t *node, *last = NULL; + token_list_t *expansion; + active_list_t *active_initial = parser->active; + + if (list == NULL) + return; + + _token_list_trim_trailing_space (list); + + node_prev = NULL; + node = list->head; + + while (node) { + + while (parser->active && parser->active->marker == node) + _parser_active_list_pop (parser); + + expansion = _glcpp_parser_expand_node (parser, node, &last); + if (expansion) { + token_node_t *n; + + for (n = node; n != last->next; n = n->next) + while (parser->active && + parser->active->marker == n) + { + _parser_active_list_pop (parser); + } + + _parser_active_list_push (parser, + node->token->value.str, + last->next); + + /* Splice expansion into list, supporting a + * simple deletion if the expansion is + * empty. */ + if (expansion->head) { + if (node_prev) + node_prev->next = expansion->head; + else + list->head = expansion->head; + expansion->tail->next = last->next; + if (last == list->tail) + list->tail = expansion->tail; + } else { + if (node_prev) + node_prev->next = last->next; + else + list->head = last->next; + if (last == list->tail) + list->tail = NULL; + } + } else { + node_prev = node; + } + node = node_prev ? node_prev->next : list->head; + } + + /* Remove any lingering effects of this invocation on the + * active list. That is, pop until the list looks like it did + * at the beginning of this function. */ + while (parser->active && parser->active != active_initial) + _parser_active_list_pop (parser); + + list->non_space_tail = list->tail; +} + +void +_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser, + token_list_t *list) +{ + if (list == NULL) + return; + + _glcpp_parser_expand_token_list (parser, list); + + _token_list_trim_trailing_space (list); + + _token_list_print (parser, list); +} + +static void +_check_for_reserved_macro_name (glcpp_parser_t *parser, YYLTYPE *loc, + const char *identifier) +{ + /* According to the GLSL specification, macro names starting with "__" + * or "GL_" are reserved for future use. So, don't allow them. + */ + if (strncmp(identifier, "__", 2) == 0) { + glcpp_error (loc, parser, "Macro names starting with \"__\" are reserved.\n"); + } + if (strncmp(identifier, "GL_", 3) == 0) { + glcpp_error (loc, parser, "Macro names starting with \"GL_\" are reserved.\n"); + } +} + +static int +_macro_equal (macro_t *a, macro_t *b) +{ + if (a->is_function != b->is_function) + return 0; + + if (a->is_function) { + if (! _string_list_equal (a->parameters, b->parameters)) + return 0; + } + + return _token_list_equal_ignoring_space (a->replacements, + b->replacements); +} + +void +_define_object_macro (glcpp_parser_t *parser, + YYLTYPE *loc, + const char *identifier, + token_list_t *replacements) +{ + macro_t *macro, *previous; + + if (loc != NULL) + _check_for_reserved_macro_name(parser, loc, identifier); + + macro = ralloc (parser, macro_t); + + macro->is_function = 0; + macro->parameters = NULL; + macro->identifier = ralloc_strdup (macro, identifier); + macro->replacements = replacements; + ralloc_steal (macro, replacements); + + previous = hash_table_find (parser->defines, identifier); + if (previous) { + if (_macro_equal (macro, previous)) { + ralloc_free (macro); + return; + } + glcpp_error (loc, parser, "Redefinition of macro %s\n", + identifier); + } + + hash_table_insert (parser->defines, macro, identifier); +} + +void +_define_function_macro (glcpp_parser_t *parser, + YYLTYPE *loc, + const char *identifier, + string_list_t *parameters, + token_list_t *replacements) +{ + macro_t *macro, *previous; + + _check_for_reserved_macro_name(parser, loc, identifier); + + macro = ralloc (parser, macro_t); + ralloc_steal (macro, parameters); + ralloc_steal (macro, replacements); + + macro->is_function = 1; + macro->parameters = parameters; + macro->identifier = ralloc_strdup (macro, identifier); + macro->replacements = replacements; + previous = hash_table_find (parser->defines, identifier); + if (previous) { + if (_macro_equal (macro, previous)) { + ralloc_free (macro); + return; + } + glcpp_error (loc, parser, "Redefinition of macro %s\n", + identifier); + } + + hash_table_insert (parser->defines, macro, identifier); +} + +static int +glcpp_parser_lex (YYSTYPE *yylval, YYLTYPE *yylloc, glcpp_parser_t *parser) +{ + token_node_t *node; + int ret; + + if (parser->lex_from_list == NULL) { + ret = glcpp_lex (yylval, yylloc, parser->scanner); + + /* XXX: This ugly block of code exists for the sole + * purpose of converting a NEWLINE token into a SPACE + * token, but only in the case where we have seen a + * function-like macro name, but have not yet seen its + * closing parenthesis. + * + * There's perhaps a more compact way to do this with + * mid-rule actions in the grammar. + * + * I'm definitely not pleased with the complexity of + * this code here. + */ + if (parser->newline_as_space) + { + if (ret == '(') { + parser->paren_count++; + } else if (ret == ')') { + parser->paren_count--; + if (parser->paren_count == 0) + parser->newline_as_space = 0; + } else if (ret == NEWLINE) { + ret = SPACE; + } else if (ret != SPACE) { + if (parser->paren_count == 0) + parser->newline_as_space = 0; + } + } + else if (parser->in_control_line) + { + if (ret == NEWLINE) + parser->in_control_line = 0; + } + else if (ret == HASH_DEFINE_OBJ || ret == HASH_DEFINE_FUNC || + ret == HASH_UNDEF || ret == HASH_IF || + ret == HASH_IFDEF || ret == HASH_IFNDEF || + ret == HASH_ELIF || ret == HASH_ELSE || + ret == HASH_ENDIF || ret == HASH) + { + parser->in_control_line = 1; + } + else if (ret == IDENTIFIER) + { + macro_t *macro; + macro = hash_table_find (parser->defines, + yylval->str); + if (macro && macro->is_function) { + parser->newline_as_space = 1; + parser->paren_count = 0; + } + } + + return ret; + } + + node = parser->lex_from_node; + + if (node == NULL) { + ralloc_free (parser->lex_from_list); + parser->lex_from_list = NULL; + return NEWLINE; + } + + *yylval = node->token->value; + ret = node->token->type; + + parser->lex_from_node = node->next; + + return ret; +} + +static void +glcpp_parser_lex_from (glcpp_parser_t *parser, token_list_t *list) +{ + token_node_t *node; + + assert (parser->lex_from_list == NULL); + + /* Copy list, eliminating any space tokens. */ + parser->lex_from_list = _token_list_create (parser); + + for (node = list->head; node; node = node->next) { + if (node->token->type == SPACE) + continue; + _token_list_append (parser->lex_from_list, node->token); + } + + ralloc_free (list); + + parser->lex_from_node = parser->lex_from_list->head; + + /* It's possible the list consisted of nothing but whitespace. */ + if (parser->lex_from_node == NULL) { + ralloc_free (parser->lex_from_list); + parser->lex_from_list = NULL; + } +} + +static void +_glcpp_parser_skip_stack_push_if (glcpp_parser_t *parser, YYLTYPE *loc, + int condition) +{ + skip_type_t current = SKIP_NO_SKIP; + skip_node_t *node; + + if (parser->skip_stack) + current = parser->skip_stack->type; + + node = ralloc (parser, skip_node_t); + node->loc = *loc; + + if (current == SKIP_NO_SKIP) { + if (condition) + node->type = SKIP_NO_SKIP; + else + node->type = SKIP_TO_ELSE; + } else { + node->type = SKIP_TO_ENDIF; + } + + node->next = parser->skip_stack; + parser->skip_stack = node; +} + +static void +_glcpp_parser_skip_stack_change_if (glcpp_parser_t *parser, YYLTYPE *loc, + const char *type, int condition) +{ + if (parser->skip_stack == NULL) { + glcpp_error (loc, parser, "%s without #if\n", type); + return; + } + + if (parser->skip_stack->type == SKIP_TO_ELSE) { + if (condition) + parser->skip_stack->type = SKIP_NO_SKIP; + } else { + parser->skip_stack->type = SKIP_TO_ENDIF; + } +} + +static void +_glcpp_parser_skip_stack_pop (glcpp_parser_t *parser, YYLTYPE *loc) +{ + skip_node_t *node; + + if (parser->skip_stack == NULL) { + glcpp_error (loc, parser, "#endif without #if\n"); + return; + } + + node = parser->skip_stack; + parser->skip_stack = node->next; + ralloc_free (node); +} diff --git a/mesalib/src/glsl/glsl_parser_extras.cpp b/mesalib/src/glsl/glsl_parser_extras.cpp index 5d66fe9d5..8faddc578 100644 --- a/mesalib/src/glsl/glsl_parser_extras.cpp +++ b/mesalib/src/glsl/glsl_parser_extras.cpp @@ -1,958 +1,958 @@ -/* - * Copyright © 2008, 2009 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 -#include -#include -#include - -extern "C" { -#include "main/core.h" /* for struct gl_context */ -} - -#include "ralloc.h" -#include "ast.h" -#include "glsl_parser_extras.h" -#include "glsl_parser.h" -#include "ir_optimization.h" -#include "loop_analysis.h" - -_mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx, - GLenum target, void *mem_ctx) -{ - switch (target) { - case GL_VERTEX_SHADER: this->target = vertex_shader; break; - case GL_FRAGMENT_SHADER: this->target = fragment_shader; break; - case GL_GEOMETRY_SHADER: this->target = geometry_shader; break; - } - - this->scanner = NULL; - this->translation_unit.make_empty(); - this->symbols = new(mem_ctx) glsl_symbol_table; - this->info_log = ralloc_strdup(mem_ctx, ""); - this->error = false; - this->loop_or_switch_nesting = NULL; - - /* Set default language version and extensions */ - this->language_version = 110; - this->es_shader = false; - this->ARB_texture_rectangle_enable = true; - - /* OpenGL ES 2.0 has different defaults from desktop GL. */ - if (ctx->API == API_OPENGLES2) { - this->language_version = 100; - this->es_shader = true; - this->ARB_texture_rectangle_enable = false; - } - - this->extensions = &ctx->Extensions; - - this->Const.MaxLights = ctx->Const.MaxLights; - this->Const.MaxClipPlanes = ctx->Const.MaxClipPlanes; - this->Const.MaxTextureUnits = ctx->Const.MaxTextureUnits; - this->Const.MaxTextureCoords = ctx->Const.MaxTextureCoordUnits; - this->Const.MaxVertexAttribs = ctx->Const.VertexProgram.MaxAttribs; - this->Const.MaxVertexUniformComponents = ctx->Const.VertexProgram.MaxUniformComponents; - this->Const.MaxVaryingFloats = ctx->Const.MaxVarying * 4; - this->Const.MaxVertexTextureImageUnits = ctx->Const.MaxVertexTextureImageUnits; - this->Const.MaxCombinedTextureImageUnits = ctx->Const.MaxCombinedTextureImageUnits; - this->Const.MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits; - this->Const.MaxFragmentUniformComponents = ctx->Const.FragmentProgram.MaxUniformComponents; - - this->Const.MaxDrawBuffers = ctx->Const.MaxDrawBuffers; - - /* Note: Once the OpenGL 3.0 'forward compatible' context or the OpenGL 3.2 - * Core context is supported, this logic will need change. Older versions of - * GLSL are no longer supported outside the compatibility contexts of 3.x. - */ - this->Const.GLSL_100ES = (ctx->API == API_OPENGLES2) - || ctx->Extensions.ARB_ES2_compatibility; - this->Const.GLSL_110 = (ctx->API == API_OPENGL); - this->Const.GLSL_120 = (ctx->API == API_OPENGL) - && (ctx->Const.GLSLVersion >= 120); - this->Const.GLSL_130 = (ctx->API == API_OPENGL) - && (ctx->Const.GLSLVersion >= 130); - - const unsigned lowest_version = - (ctx->API == API_OPENGLES2) || ctx->Extensions.ARB_ES2_compatibility - ? 100 : 110; - const unsigned highest_version = - (ctx->API == API_OPENGL) ? ctx->Const.GLSLVersion : 100; - char *supported = ralloc_strdup(this, ""); - - for (unsigned ver = lowest_version; ver <= highest_version; ver += 10) { - const char *const prefix = (ver == lowest_version) - ? "" - : ((ver == highest_version) ? ", and " : ", "); - - ralloc_asprintf_append(& supported, "%s%d.%02d%s", - prefix, - ver / 100, ver % 100, - (ver == 100) ? " ES" : ""); - } - - this->supported_version_string = supported; -} - -const char * -_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target) -{ - switch (target) { - case vertex_shader: return "vertex"; - case fragment_shader: return "fragment"; - case geometry_shader: return "geometry"; - } - - assert(!"Should not get here."); - return "unknown"; -} - - -void -_mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...) -{ - va_list ap; - - state->error = true; - - assert(state->info_log != NULL); - ralloc_asprintf_append(&state->info_log, "%u:%u(%u): error: ", - locp->source, - locp->first_line, - locp->first_column); - va_start(ap, fmt); - ralloc_vasprintf_append(&state->info_log, fmt, ap); - va_end(ap); - ralloc_strcat(&state->info_log, "\n"); -} - - -void -_mesa_glsl_warning(const YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...) -{ - va_list ap; - - assert(state->info_log != NULL); - ralloc_asprintf_append(&state->info_log, "%u:%u(%u): warning: ", - locp->source, - locp->first_line, - locp->first_column); - va_start(ap, fmt); - ralloc_vasprintf_append(&state->info_log, fmt, ap); - va_end(ap); - ralloc_strcat(&state->info_log, "\n"); -} - - -/** - * Enum representing the possible behaviors that can be specified in - * an #extension directive. - */ -enum ext_behavior { - extension_disable, - extension_enable, - extension_require, - extension_warn -}; - -/** - * Element type for _mesa_glsl_supported_extensions - */ -struct _mesa_glsl_extension { - /** - * Name of the extension when referred to in a GLSL extension - * statement - */ - const char *name; - - /** True if this extension is available to vertex shaders */ - bool avail_in_VS; - - /** True if this extension is available to geometry shaders */ - bool avail_in_GS; - - /** True if this extension is available to fragment shaders */ - bool avail_in_FS; - - /** True if this extension is available to desktop GL shaders */ - bool avail_in_GL; - - /** True if this extension is available to GLES shaders */ - bool avail_in_ES; - - /** - * Flag in the gl_extensions struct indicating whether this - * extension is supported by the driver, or - * &gl_extensions::dummy_true if supported by all drivers. - * - * Note: the type (GLboolean gl_extensions::*) is a "pointer to - * member" type, the type-safe alternative to the "offsetof" macro. - * In a nutshell: - * - * - foo bar::* p declares p to be an "offset" to a field of type - * foo that exists within struct bar - * - &bar::baz computes the "offset" of field baz within struct bar - * - x.*p accesses the field of x that exists at "offset" p - * - x->*p is equivalent to (*x).*p - */ - const GLboolean gl_extensions::* supported_flag; - - /** - * Flag in the _mesa_glsl_parse_state struct that should be set - * when this extension is enabled. - * - * See note in _mesa_glsl_extension::supported_flag about "pointer - * to member" types. - */ - bool _mesa_glsl_parse_state::* enable_flag; - - /** - * Flag in the _mesa_glsl_parse_state struct that should be set - * when the shader requests "warn" behavior for this extension. - * - * See note in _mesa_glsl_extension::supported_flag about "pointer - * to member" types. - */ - bool _mesa_glsl_parse_state::* warn_flag; - - - bool compatible_with_state(const _mesa_glsl_parse_state *state) const; - void set_flags(_mesa_glsl_parse_state *state, ext_behavior behavior) const; -}; - -#define EXT(NAME, VS, GS, FS, GL, ES, SUPPORTED_FLAG) \ - { "GL_" #NAME, VS, GS, FS, GL, ES, &gl_extensions::SUPPORTED_FLAG, \ - &_mesa_glsl_parse_state::NAME##_enable, \ - &_mesa_glsl_parse_state::NAME##_warn } - -/** - * Table of extensions that can be enabled/disabled within a shader, - * and the conditions under which they are supported. - */ -static const _mesa_glsl_extension _mesa_glsl_supported_extensions[] = { - /* target availability API availability */ - /* name VS GS FS GL ES supported flag */ - EXT(ARB_conservative_depth, true, false, true, true, false, AMD_conservative_depth), - EXT(ARB_draw_buffers, false, false, true, true, false, dummy_true), - EXT(ARB_draw_instanced, true, false, false, true, false, ARB_draw_instanced), - EXT(ARB_explicit_attrib_location, true, false, true, true, false, ARB_explicit_attrib_location), - EXT(ARB_fragment_coord_conventions, true, false, true, true, false, ARB_fragment_coord_conventions), - EXT(ARB_texture_rectangle, true, false, true, true, false, dummy_true), - EXT(EXT_texture_array, true, false, true, true, false, EXT_texture_array), - EXT(ARB_shader_texture_lod, true, false, true, true, false, ARB_shader_texture_lod), - EXT(ARB_shader_stencil_export, false, false, true, true, false, ARB_shader_stencil_export), - EXT(AMD_conservative_depth, true, false, true, true, false, AMD_conservative_depth), - EXT(AMD_shader_stencil_export, false, false, true, true, false, ARB_shader_stencil_export), - EXT(OES_texture_3D, true, false, true, false, true, EXT_texture3D), -}; - -#undef EXT - - -/** - * Determine whether a given extension is compatible with the target, - * API, and extension information in the current parser state. - */ -bool _mesa_glsl_extension::compatible_with_state(const _mesa_glsl_parse_state * - state) const -{ - /* Check that this extension matches the type of shader we are - * compiling to. - */ - switch (state->target) { - case vertex_shader: - if (!this->avail_in_VS) { - return false; - } - break; - case geometry_shader: - if (!this->avail_in_GS) { - return false; - } - break; - case fragment_shader: - if (!this->avail_in_FS) { - return false; - } - break; - default: - assert (!"Unrecognized shader target"); - return false; - } - - /* Check that this extension matches whether we are compiling - * for desktop GL or GLES. - */ - if (state->es_shader) { - if (!this->avail_in_ES) return false; - } else { - if (!this->avail_in_GL) return false; - } - - /* Check that this extension is supported by the OpenGL - * implementation. - * - * Note: the ->* operator indexes into state->extensions by the - * offset this->supported_flag. See - * _mesa_glsl_extension::supported_flag for more info. - */ - return state->extensions->*(this->supported_flag); -} - -/** - * Set the appropriate flags in the parser state to establish the - * given behavior for this extension. - */ -void _mesa_glsl_extension::set_flags(_mesa_glsl_parse_state *state, - ext_behavior behavior) const -{ - /* Note: the ->* operator indexes into state by the - * offsets this->enable_flag and this->warn_flag. See - * _mesa_glsl_extension::supported_flag for more info. - */ - state->*(this->enable_flag) = (behavior != extension_disable); - state->*(this->warn_flag) = (behavior == extension_warn); -} - -/** - * Find an extension by name in _mesa_glsl_supported_extensions. If - * the name is not found, return NULL. - */ -static const _mesa_glsl_extension *find_extension(const char *name) -{ - for (unsigned i = 0; i < Elements(_mesa_glsl_supported_extensions); ++i) { - if (strcmp(name, _mesa_glsl_supported_extensions[i].name) == 0) { - return &_mesa_glsl_supported_extensions[i]; - } - } - return NULL; -} - - -bool -_mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, - const char *behavior_string, YYLTYPE *behavior_locp, - _mesa_glsl_parse_state *state) -{ - ext_behavior behavior; - if (strcmp(behavior_string, "warn") == 0) { - behavior = extension_warn; - } else if (strcmp(behavior_string, "require") == 0) { - behavior = extension_require; - } else if (strcmp(behavior_string, "enable") == 0) { - behavior = extension_enable; - } else if (strcmp(behavior_string, "disable") == 0) { - behavior = extension_disable; - } else { - _mesa_glsl_error(behavior_locp, state, - "Unknown extension behavior `%s'", - behavior_string); - return false; - } - - if (strcmp(name, "all") == 0) { - if ((behavior == extension_enable) || (behavior == extension_require)) { - _mesa_glsl_error(name_locp, state, "Cannot %s all extensions", - (behavior == extension_enable) - ? "enable" : "require"); - return false; - } else { - for (unsigned i = 0; - i < Elements(_mesa_glsl_supported_extensions); ++i) { - const _mesa_glsl_extension *extension - = &_mesa_glsl_supported_extensions[i]; - if (extension->compatible_with_state(state)) { - _mesa_glsl_supported_extensions[i].set_flags(state, behavior); - } - } - } - } else { - const _mesa_glsl_extension *extension = find_extension(name); - if (extension && extension->compatible_with_state(state)) { - extension->set_flags(state, behavior); - } else { - static const char *const fmt = "extension `%s' unsupported in %s shader"; - - if (behavior == extension_require) { - _mesa_glsl_error(name_locp, state, fmt, - name, _mesa_glsl_shader_target_name(state->target)); - return false; - } else { - _mesa_glsl_warning(name_locp, state, fmt, - name, _mesa_glsl_shader_target_name(state->target)); - } - } - } - - return true; -} - -void -_mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q) -{ - if (q->flags.q.constant) - printf("const "); - - if (q->flags.q.invariant) - printf("invariant "); - - if (q->flags.q.attribute) - printf("attribute "); - - if (q->flags.q.varying) - printf("varying "); - - if (q->flags.q.in && q->flags.q.out) - printf("inout "); - else { - if (q->flags.q.in) - printf("in "); - - if (q->flags.q.out) - printf("out "); - } - - if (q->flags.q.centroid) - printf("centroid "); - if (q->flags.q.uniform) - printf("uniform "); - if (q->flags.q.smooth) - printf("smooth "); - if (q->flags.q.flat) - printf("flat "); - if (q->flags.q.noperspective) - printf("noperspective "); -} - - -void -ast_node::print(void) const -{ - printf("unhandled node "); -} - - -ast_node::ast_node(void) -{ - this->location.source = 0; - this->location.line = 0; - this->location.column = 0; -} - - -static void -ast_opt_array_size_print(bool is_array, const ast_expression *array_size) -{ - if (is_array) { - printf("[ "); - - if (array_size) - array_size->print(); - - printf("] "); - } -} - - -void -ast_compound_statement::print(void) const -{ - printf("{\n"); - - foreach_list_const(n, &this->statements) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf("}\n"); -} - - -ast_compound_statement::ast_compound_statement(int new_scope, - ast_node *statements) -{ - this->new_scope = new_scope; - - if (statements != NULL) { - this->statements.push_degenerate_list_at_head(&statements->link); - } -} - - -void -ast_expression::print(void) const -{ - switch (oper) { - case ast_assign: - case ast_mul_assign: - case ast_div_assign: - case ast_mod_assign: - case ast_add_assign: - case ast_sub_assign: - case ast_ls_assign: - case ast_rs_assign: - case ast_and_assign: - case ast_xor_assign: - case ast_or_assign: - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - subexpressions[1]->print(); - break; - - case ast_field_selection: - subexpressions[0]->print(); - printf(". %s ", primary_expression.identifier); - break; - - case ast_plus: - case ast_neg: - case ast_bit_not: - case ast_logic_not: - case ast_pre_inc: - case ast_pre_dec: - printf("%s ", operator_string(oper)); - subexpressions[0]->print(); - break; - - case ast_post_inc: - case ast_post_dec: - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - break; - - case ast_conditional: - subexpressions[0]->print(); - printf("? "); - subexpressions[1]->print(); - printf(": "); - subexpressions[2]->print(); - break; - - case ast_array_index: - subexpressions[0]->print(); - printf("[ "); - subexpressions[1]->print(); - printf("] "); - break; - - case ast_function_call: { - subexpressions[0]->print(); - printf("( "); - - foreach_list_const (n, &this->expressions) { - if (n != this->expressions.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf(") "); - break; - } - - case ast_identifier: - printf("%s ", primary_expression.identifier); - break; - - case ast_int_constant: - printf("%d ", primary_expression.int_constant); - break; - - case ast_uint_constant: - printf("%u ", primary_expression.uint_constant); - break; - - case ast_float_constant: - printf("%f ", primary_expression.float_constant); - break; - - case ast_bool_constant: - printf("%s ", - primary_expression.bool_constant - ? "true" : "false"); - break; - - case ast_sequence: { - printf("( "); - foreach_list_const(n, & this->expressions) { - if (n != this->expressions.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf(") "); - break; - } - - default: - assert(0); - break; - } -} - -ast_expression::ast_expression(int oper, - ast_expression *ex0, - ast_expression *ex1, - ast_expression *ex2) -{ - this->oper = ast_operators(oper); - this->subexpressions[0] = ex0; - this->subexpressions[1] = ex1; - this->subexpressions[2] = ex2; -} - - -void -ast_expression_statement::print(void) const -{ - if (expression) - expression->print(); - - printf("; "); -} - - -ast_expression_statement::ast_expression_statement(ast_expression *ex) : - expression(ex) -{ - /* empty */ -} - - -void -ast_function::print(void) const -{ - return_type->print(); - printf(" %s (", identifier); - - foreach_list_const(n, & this->parameters) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf(")"); -} - - -ast_function::ast_function(void) - : is_definition(false), signature(NULL) -{ - /* empty */ -} - - -void -ast_fully_specified_type::print(void) const -{ - _mesa_ast_type_qualifier_print(& qualifier); - specifier->print(); -} - - -void -ast_parameter_declarator::print(void) const -{ - type->print(); - if (identifier) - printf("%s ", identifier); - ast_opt_array_size_print(is_array, array_size); -} - - -void -ast_function_definition::print(void) const -{ - prototype->print(); - body->print(); -} - - -void -ast_declaration::print(void) const -{ - printf("%s ", identifier); - ast_opt_array_size_print(is_array, array_size); - - if (initializer) { - printf("= "); - initializer->print(); - } -} - - -ast_declaration::ast_declaration(char *identifier, int is_array, - ast_expression *array_size, - ast_expression *initializer) -{ - this->identifier = identifier; - this->is_array = is_array; - this->array_size = array_size; - this->initializer = initializer; -} - - -void -ast_declarator_list::print(void) const -{ - assert(type || invariant); - - if (type) - type->print(); - else - printf("invariant "); - - foreach_list_const (ptr, & this->declarations) { - if (ptr != this->declarations.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, ptr, link); - ast->print(); - } - - printf("; "); -} - - -ast_declarator_list::ast_declarator_list(ast_fully_specified_type *type) -{ - this->type = type; - this->invariant = false; -} - -void -ast_jump_statement::print(void) const -{ - switch (mode) { - case ast_continue: - printf("continue; "); - break; - case ast_break: - printf("break; "); - break; - case ast_return: - printf("return "); - if (opt_return_value) - opt_return_value->print(); - - printf("; "); - break; - case ast_discard: - printf("discard; "); - break; - } -} - - -ast_jump_statement::ast_jump_statement(int mode, ast_expression *return_value) -{ - this->mode = ast_jump_modes(mode); - - if (mode == ast_return) - opt_return_value = return_value; -} - - -void -ast_selection_statement::print(void) const -{ - printf("if ( "); - condition->print(); - printf(") "); - - then_statement->print(); - - if (else_statement) { - printf("else "); - else_statement->print(); - } - -} - - -ast_selection_statement::ast_selection_statement(ast_expression *condition, - ast_node *then_statement, - ast_node *else_statement) -{ - this->condition = condition; - this->then_statement = then_statement; - this->else_statement = else_statement; -} - - -void -ast_iteration_statement::print(void) const -{ - switch (mode) { - case ast_for: - printf("for( "); - if (init_statement) - init_statement->print(); - printf("; "); - - if (condition) - condition->print(); - printf("; "); - - if (rest_expression) - rest_expression->print(); - printf(") "); - - body->print(); - break; - - case ast_while: - printf("while ( "); - if (condition) - condition->print(); - printf(") "); - body->print(); - break; - - case ast_do_while: - printf("do "); - body->print(); - printf("while ( "); - if (condition) - condition->print(); - printf("); "); - break; - } -} - - -ast_iteration_statement::ast_iteration_statement(int mode, - ast_node *init, - ast_node *condition, - ast_expression *rest_expression, - ast_node *body) -{ - this->mode = ast_iteration_modes(mode); - this->init_statement = init; - this->condition = condition; - this->rest_expression = rest_expression; - this->body = body; -} - - -void -ast_struct_specifier::print(void) const -{ - printf("struct %s { ", name); - foreach_list_const(n, &this->declarations) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf("} "); -} - - -ast_struct_specifier::ast_struct_specifier(char *identifier, - ast_node *declarator_list) -{ - if (identifier == NULL) { - static unsigned anon_count = 1; - identifier = ralloc_asprintf(this, "#anon_struct_%04x", anon_count); - anon_count++; - } - name = identifier; - this->declarations.push_degenerate_list_at_head(&declarator_list->link); -} - -bool -do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations) -{ - GLboolean progress = GL_FALSE; - - progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress; - - if (linked) { - progress = do_function_inlining(ir) || progress; - progress = do_dead_functions(ir) || progress; - progress = do_structure_splitting(ir) || progress; - } - progress = do_if_simplification(ir) || progress; - progress = do_discard_simplification(ir) || progress; - progress = do_copy_propagation(ir) || progress; - progress = do_copy_propagation_elements(ir) || progress; - if (linked) - progress = do_dead_code(ir) || progress; - else - progress = do_dead_code_unlinked(ir) || progress; - progress = do_dead_code_local(ir) || progress; - progress = do_tree_grafting(ir) || progress; - progress = do_constant_propagation(ir) || progress; - if (linked) - progress = do_constant_variable(ir) || progress; - else - progress = do_constant_variable_unlinked(ir) || progress; - progress = do_constant_folding(ir) || progress; - progress = do_algebraic(ir) || progress; - progress = do_lower_jumps(ir) || progress; - progress = do_vec_index_to_swizzle(ir) || progress; - progress = do_swizzle_swizzle(ir) || progress; - progress = do_noop_swizzle(ir) || progress; - - progress = optimize_redundant_jumps(ir) || progress; - - loop_state *ls = analyze_loop_variables(ir); - if (ls->loop_found) { - progress = set_loop_controls(ir, ls) || progress; - progress = unroll_loops(ir, ls, max_unroll_iterations) || progress; - } - delete ls; - - return progress; -} - -extern "C" { - -/** - * To be called at GL teardown time, this frees compiler datastructures. - * - * After calling this, any previously compiled shaders and shader - * programs would be invalid. So this should happen at approximately - * program exit. - */ -void -_mesa_destroy_shader_compiler(void) -{ - _mesa_destroy_shader_compiler_caches(); - - _mesa_glsl_release_types(); -} - -/** - * Releases compiler caches to trade off performance for memory. - * - * Intended to be used with glReleaseShaderCompiler(). - */ -void -_mesa_destroy_shader_compiler_caches(void) -{ - _mesa_glsl_release_functions(); -} - -} +/* + * Copyright © 2008, 2009 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 +#include +#include +#include + +extern "C" { +#include "main/core.h" /* for struct gl_context */ +} + +#include "ralloc.h" +#include "ast.h" +#include "glsl_parser_extras.h" +#include "glsl_parser.h" +#include "ir_optimization.h" +#include "loop_analysis.h" + +_mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx, + GLenum target, void *mem_ctx) +{ + switch (target) { + case GL_VERTEX_SHADER: this->target = vertex_shader; break; + case GL_FRAGMENT_SHADER: this->target = fragment_shader; break; + case GL_GEOMETRY_SHADER: this->target = geometry_shader; break; + } + + this->scanner = NULL; + this->translation_unit.make_empty(); + this->symbols = new(mem_ctx) glsl_symbol_table; + this->info_log = ralloc_strdup(mem_ctx, ""); + this->error = false; + this->loop_or_switch_nesting = NULL; + + /* Set default language version and extensions */ + this->language_version = 110; + this->es_shader = false; + this->ARB_texture_rectangle_enable = true; + + /* OpenGL ES 2.0 has different defaults from desktop GL. */ + if (ctx->API == API_OPENGLES2) { + this->language_version = 100; + this->es_shader = true; + this->ARB_texture_rectangle_enable = false; + } + + this->extensions = &ctx->Extensions; + + this->Const.MaxLights = ctx->Const.MaxLights; + this->Const.MaxClipPlanes = ctx->Const.MaxClipPlanes; + this->Const.MaxTextureUnits = ctx->Const.MaxTextureUnits; + this->Const.MaxTextureCoords = ctx->Const.MaxTextureCoordUnits; + this->Const.MaxVertexAttribs = ctx->Const.VertexProgram.MaxAttribs; + this->Const.MaxVertexUniformComponents = ctx->Const.VertexProgram.MaxUniformComponents; + this->Const.MaxVaryingFloats = ctx->Const.MaxVarying * 4; + this->Const.MaxVertexTextureImageUnits = ctx->Const.MaxVertexTextureImageUnits; + this->Const.MaxCombinedTextureImageUnits = ctx->Const.MaxCombinedTextureImageUnits; + this->Const.MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits; + this->Const.MaxFragmentUniformComponents = ctx->Const.FragmentProgram.MaxUniformComponents; + + this->Const.MaxDrawBuffers = ctx->Const.MaxDrawBuffers; + + /* Note: Once the OpenGL 3.0 'forward compatible' context or the OpenGL 3.2 + * Core context is supported, this logic will need change. Older versions of + * GLSL are no longer supported outside the compatibility contexts of 3.x. + */ + this->Const.GLSL_100ES = (ctx->API == API_OPENGLES2) + || ctx->Extensions.ARB_ES2_compatibility; + this->Const.GLSL_110 = (ctx->API == API_OPENGL); + this->Const.GLSL_120 = (ctx->API == API_OPENGL) + && (ctx->Const.GLSLVersion >= 120); + this->Const.GLSL_130 = (ctx->API == API_OPENGL) + && (ctx->Const.GLSLVersion >= 130); + + const unsigned lowest_version = + (ctx->API == API_OPENGLES2) || ctx->Extensions.ARB_ES2_compatibility + ? 100 : 110; + const unsigned highest_version = + (ctx->API == API_OPENGL) ? ctx->Const.GLSLVersion : 100; + char *supported = ralloc_strdup(this, ""); + + for (unsigned ver = lowest_version; ver <= highest_version; ver += 10) { + const char *const prefix = (ver == lowest_version) + ? "" + : ((ver == highest_version) ? ", and " : ", "); + + ralloc_asprintf_append(& supported, "%s%d.%02d%s", + prefix, + ver / 100, ver % 100, + (ver == 100) ? " ES" : ""); + } + + this->supported_version_string = supported; +} + +const char * +_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target) +{ + switch (target) { + case vertex_shader: return "vertex"; + case fragment_shader: return "fragment"; + case geometry_shader: return "geometry"; + } + + assert(!"Should not get here."); + return "unknown"; +} + + +void +_mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, + const char *fmt, ...) +{ + va_list ap; + + state->error = true; + + assert(state->info_log != NULL); + ralloc_asprintf_append(&state->info_log, "%u:%u(%u): error: ", + locp->source, + locp->first_line, + locp->first_column); + va_start(ap, fmt); + ralloc_vasprintf_append(&state->info_log, fmt, ap); + va_end(ap); + ralloc_strcat(&state->info_log, "\n"); +} + + +void +_mesa_glsl_warning(const YYLTYPE *locp, _mesa_glsl_parse_state *state, + const char *fmt, ...) +{ + va_list ap; + + assert(state->info_log != NULL); + ralloc_asprintf_append(&state->info_log, "%u:%u(%u): warning: ", + locp->source, + locp->first_line, + locp->first_column); + va_start(ap, fmt); + ralloc_vasprintf_append(&state->info_log, fmt, ap); + va_end(ap); + ralloc_strcat(&state->info_log, "\n"); +} + + +/** + * Enum representing the possible behaviors that can be specified in + * an #extension directive. + */ +enum ext_behavior { + extension_disable, + extension_enable, + extension_require, + extension_warn +}; + +/** + * Element type for _mesa_glsl_supported_extensions + */ +struct _mesa_glsl_extension { + /** + * Name of the extension when referred to in a GLSL extension + * statement + */ + const char *name; + + /** True if this extension is available to vertex shaders */ + bool avail_in_VS; + + /** True if this extension is available to geometry shaders */ + bool avail_in_GS; + + /** True if this extension is available to fragment shaders */ + bool avail_in_FS; + + /** True if this extension is available to desktop GL shaders */ + bool avail_in_GL; + + /** True if this extension is available to GLES shaders */ + bool avail_in_ES; + + /** + * Flag in the gl_extensions struct indicating whether this + * extension is supported by the driver, or + * &gl_extensions::dummy_true if supported by all drivers. + * + * Note: the type (GLboolean gl_extensions::*) is a "pointer to + * member" type, the type-safe alternative to the "offsetof" macro. + * In a nutshell: + * + * - foo bar::* p declares p to be an "offset" to a field of type + * foo that exists within struct bar + * - &bar::baz computes the "offset" of field baz within struct bar + * - x.*p accesses the field of x that exists at "offset" p + * - x->*p is equivalent to (*x).*p + */ + const GLboolean gl_extensions::* supported_flag; + + /** + * Flag in the _mesa_glsl_parse_state struct that should be set + * when this extension is enabled. + * + * See note in _mesa_glsl_extension::supported_flag about "pointer + * to member" types. + */ + bool _mesa_glsl_parse_state::* enable_flag; + + /** + * Flag in the _mesa_glsl_parse_state struct that should be set + * when the shader requests "warn" behavior for this extension. + * + * See note in _mesa_glsl_extension::supported_flag about "pointer + * to member" types. + */ + bool _mesa_glsl_parse_state::* warn_flag; + + + bool compatible_with_state(const _mesa_glsl_parse_state *state) const; + void set_flags(_mesa_glsl_parse_state *state, ext_behavior behavior) const; +}; + +#define EXT(NAME, VS, GS, FS, GL, ES, SUPPORTED_FLAG) \ + { "GL_" #NAME, VS, GS, FS, GL, ES, &gl_extensions::SUPPORTED_FLAG, \ + &_mesa_glsl_parse_state::NAME##_enable, \ + &_mesa_glsl_parse_state::NAME##_warn } + +/** + * Table of extensions that can be enabled/disabled within a shader, + * and the conditions under which they are supported. + */ +static const _mesa_glsl_extension _mesa_glsl_supported_extensions[] = { + /* target availability API availability */ + /* name VS GS FS GL ES supported flag */ + EXT(ARB_conservative_depth, true, false, true, true, false, AMD_conservative_depth), + EXT(ARB_draw_buffers, false, false, true, true, false, dummy_true), + EXT(ARB_draw_instanced, true, false, false, true, false, ARB_draw_instanced), + EXT(ARB_explicit_attrib_location, true, false, true, true, false, ARB_explicit_attrib_location), + EXT(ARB_fragment_coord_conventions, true, false, true, true, false, ARB_fragment_coord_conventions), + EXT(ARB_texture_rectangle, true, false, true, true, false, dummy_true), + EXT(EXT_texture_array, true, false, true, true, false, EXT_texture_array), + EXT(ARB_shader_texture_lod, true, false, true, true, false, ARB_shader_texture_lod), + EXT(ARB_shader_stencil_export, false, false, true, true, false, ARB_shader_stencil_export), + EXT(AMD_conservative_depth, true, false, true, true, false, AMD_conservative_depth), + EXT(AMD_shader_stencil_export, false, false, true, true, false, ARB_shader_stencil_export), + EXT(OES_texture_3D, true, false, true, false, true, EXT_texture3D), +}; + +#undef EXT + + +/** + * Determine whether a given extension is compatible with the target, + * API, and extension information in the current parser state. + */ +bool _mesa_glsl_extension::compatible_with_state(const _mesa_glsl_parse_state * + state) const +{ + /* Check that this extension matches the type of shader we are + * compiling to. + */ + switch (state->target) { + case vertex_shader: + if (!this->avail_in_VS) { + return false; + } + break; + case geometry_shader: + if (!this->avail_in_GS) { + return false; + } + break; + case fragment_shader: + if (!this->avail_in_FS) { + return false; + } + break; + default: + assert (!"Unrecognized shader target"); + return false; + } + + /* Check that this extension matches whether we are compiling + * for desktop GL or GLES. + */ + if (state->es_shader) { + if (!this->avail_in_ES) return false; + } else { + if (!this->avail_in_GL) return false; + } + + /* Check that this extension is supported by the OpenGL + * implementation. + * + * Note: the ->* operator indexes into state->extensions by the + * offset this->supported_flag. See + * _mesa_glsl_extension::supported_flag for more info. + */ + return state->extensions->*(this->supported_flag); +} + +/** + * Set the appropriate flags in the parser state to establish the + * given behavior for this extension. + */ +void _mesa_glsl_extension::set_flags(_mesa_glsl_parse_state *state, + ext_behavior behavior) const +{ + /* Note: the ->* operator indexes into state by the + * offsets this->enable_flag and this->warn_flag. See + * _mesa_glsl_extension::supported_flag for more info. + */ + state->*(this->enable_flag) = (behavior != extension_disable); + state->*(this->warn_flag) = (behavior == extension_warn); +} + +/** + * Find an extension by name in _mesa_glsl_supported_extensions. If + * the name is not found, return NULL. + */ +static const _mesa_glsl_extension *find_extension(const char *name) +{ + for (unsigned i = 0; i < Elements(_mesa_glsl_supported_extensions); ++i) { + if (strcmp(name, _mesa_glsl_supported_extensions[i].name) == 0) { + return &_mesa_glsl_supported_extensions[i]; + } + } + return NULL; +} + + +bool +_mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, + const char *behavior_string, YYLTYPE *behavior_locp, + _mesa_glsl_parse_state *state) +{ + ext_behavior behavior; + if (strcmp(behavior_string, "warn") == 0) { + behavior = extension_warn; + } else if (strcmp(behavior_string, "require") == 0) { + behavior = extension_require; + } else if (strcmp(behavior_string, "enable") == 0) { + behavior = extension_enable; + } else if (strcmp(behavior_string, "disable") == 0) { + behavior = extension_disable; + } else { + _mesa_glsl_error(behavior_locp, state, + "Unknown extension behavior `%s'", + behavior_string); + return false; + } + + if (strcmp(name, "all") == 0) { + if ((behavior == extension_enable) || (behavior == extension_require)) { + _mesa_glsl_error(name_locp, state, "Cannot %s all extensions", + (behavior == extension_enable) + ? "enable" : "require"); + return false; + } else { + for (unsigned i = 0; + i < Elements(_mesa_glsl_supported_extensions); ++i) { + const _mesa_glsl_extension *extension + = &_mesa_glsl_supported_extensions[i]; + if (extension->compatible_with_state(state)) { + _mesa_glsl_supported_extensions[i].set_flags(state, behavior); + } + } + } + } else { + const _mesa_glsl_extension *extension = find_extension(name); + if (extension && extension->compatible_with_state(state)) { + extension->set_flags(state, behavior); + } else { + static const char *const fmt = "extension `%s' unsupported in %s shader"; + + if (behavior == extension_require) { + _mesa_glsl_error(name_locp, state, fmt, + name, _mesa_glsl_shader_target_name(state->target)); + return false; + } else { + _mesa_glsl_warning(name_locp, state, fmt, + name, _mesa_glsl_shader_target_name(state->target)); + } + } + } + + return true; +} + +void +_mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q) +{ + if (q->flags.q.constant) + printf("const "); + + if (q->flags.q.invariant) + printf("invariant "); + + if (q->flags.q.attribute) + printf("attribute "); + + if (q->flags.q.varying) + printf("varying "); + + if (q->flags.q.in && q->flags.q.out) + printf("inout "); + else { + if (q->flags.q.in) + printf("in "); + + if (q->flags.q.out) + printf("out "); + } + + if (q->flags.q.centroid) + printf("centroid "); + if (q->flags.q.uniform) + printf("uniform "); + if (q->flags.q.smooth) + printf("smooth "); + if (q->flags.q.flat) + printf("flat "); + if (q->flags.q.noperspective) + printf("noperspective "); +} + + +void +ast_node::print(void) const +{ + printf("unhandled node "); +} + + +ast_node::ast_node(void) +{ + this->location.source = 0; + this->location.line = 0; + this->location.column = 0; +} + + +static void +ast_opt_array_size_print(bool is_array, const ast_expression *array_size) +{ + if (is_array) { + printf("[ "); + + if (array_size) + array_size->print(); + + printf("] "); + } +} + + +void +ast_compound_statement::print(void) const +{ + printf("{\n"); + + foreach_list_const(n, &this->statements) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + + printf("}\n"); +} + + +ast_compound_statement::ast_compound_statement(int new_scope, + ast_node *statements) +{ + this->new_scope = new_scope; + + if (statements != NULL) { + this->statements.push_degenerate_list_at_head(&statements->link); + } +} + + +void +ast_expression::print(void) const +{ + switch (oper) { + case ast_assign: + case ast_mul_assign: + case ast_div_assign: + case ast_mod_assign: + case ast_add_assign: + case ast_sub_assign: + case ast_ls_assign: + case ast_rs_assign: + case ast_and_assign: + case ast_xor_assign: + case ast_or_assign: + subexpressions[0]->print(); + printf("%s ", operator_string(oper)); + subexpressions[1]->print(); + break; + + case ast_field_selection: + subexpressions[0]->print(); + printf(". %s ", primary_expression.identifier); + break; + + case ast_plus: + case ast_neg: + case ast_bit_not: + case ast_logic_not: + case ast_pre_inc: + case ast_pre_dec: + printf("%s ", operator_string(oper)); + subexpressions[0]->print(); + break; + + case ast_post_inc: + case ast_post_dec: + subexpressions[0]->print(); + printf("%s ", operator_string(oper)); + break; + + case ast_conditional: + subexpressions[0]->print(); + printf("? "); + subexpressions[1]->print(); + printf(": "); + subexpressions[2]->print(); + break; + + case ast_array_index: + subexpressions[0]->print(); + printf("[ "); + subexpressions[1]->print(); + printf("] "); + break; + + case ast_function_call: { + subexpressions[0]->print(); + printf("( "); + + foreach_list_const (n, &this->expressions) { + if (n != this->expressions.get_head()) + printf(", "); + + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + + printf(") "); + break; + } + + case ast_identifier: + printf("%s ", primary_expression.identifier); + break; + + case ast_int_constant: + printf("%d ", primary_expression.int_constant); + break; + + case ast_uint_constant: + printf("%u ", primary_expression.uint_constant); + break; + + case ast_float_constant: + printf("%f ", primary_expression.float_constant); + break; + + case ast_bool_constant: + printf("%s ", + primary_expression.bool_constant + ? "true" : "false"); + break; + + case ast_sequence: { + printf("( "); + foreach_list_const(n, & this->expressions) { + if (n != this->expressions.get_head()) + printf(", "); + + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + printf(") "); + break; + } + + default: + assert(0); + break; + } +} + +ast_expression::ast_expression(int oper, + ast_expression *ex0, + ast_expression *ex1, + ast_expression *ex2) +{ + this->oper = ast_operators(oper); + this->subexpressions[0] = ex0; + this->subexpressions[1] = ex1; + this->subexpressions[2] = ex2; +} + + +void +ast_expression_statement::print(void) const +{ + if (expression) + expression->print(); + + printf("; "); +} + + +ast_expression_statement::ast_expression_statement(ast_expression *ex) : + expression(ex) +{ + /* empty */ +} + + +void +ast_function::print(void) const +{ + return_type->print(); + printf(" %s (", identifier); + + foreach_list_const(n, & this->parameters) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + + printf(")"); +} + + +ast_function::ast_function(void) + : is_definition(false), signature(NULL) +{ + /* empty */ +} + + +void +ast_fully_specified_type::print(void) const +{ + _mesa_ast_type_qualifier_print(& qualifier); + specifier->print(); +} + + +void +ast_parameter_declarator::print(void) const +{ + type->print(); + if (identifier) + printf("%s ", identifier); + ast_opt_array_size_print(is_array, array_size); +} + + +void +ast_function_definition::print(void) const +{ + prototype->print(); + body->print(); +} + + +void +ast_declaration::print(void) const +{ + printf("%s ", identifier); + ast_opt_array_size_print(is_array, array_size); + + if (initializer) { + printf("= "); + initializer->print(); + } +} + + +ast_declaration::ast_declaration(char *identifier, int is_array, + ast_expression *array_size, + ast_expression *initializer) +{ + this->identifier = identifier; + this->is_array = is_array; + this->array_size = array_size; + this->initializer = initializer; +} + + +void +ast_declarator_list::print(void) const +{ + assert(type || invariant); + + if (type) + type->print(); + else + printf("invariant "); + + foreach_list_const (ptr, & this->declarations) { + if (ptr != this->declarations.get_head()) + printf(", "); + + ast_node *ast = exec_node_data(ast_node, ptr, link); + ast->print(); + } + + printf("; "); +} + + +ast_declarator_list::ast_declarator_list(ast_fully_specified_type *type) +{ + this->type = type; + this->invariant = false; +} + +void +ast_jump_statement::print(void) const +{ + switch (mode) { + case ast_continue: + printf("continue; "); + break; + case ast_break: + printf("break; "); + break; + case ast_return: + printf("return "); + if (opt_return_value) + opt_return_value->print(); + + printf("; "); + break; + case ast_discard: + printf("discard; "); + break; + } +} + + +ast_jump_statement::ast_jump_statement(int mode, ast_expression *return_value) +{ + this->mode = ast_jump_modes(mode); + + if (mode == ast_return) + opt_return_value = return_value; +} + + +void +ast_selection_statement::print(void) const +{ + printf("if ( "); + condition->print(); + printf(") "); + + then_statement->print(); + + if (else_statement) { + printf("else "); + else_statement->print(); + } + +} + + +ast_selection_statement::ast_selection_statement(ast_expression *condition, + ast_node *then_statement, + ast_node *else_statement) +{ + this->condition = condition; + this->then_statement = then_statement; + this->else_statement = else_statement; +} + + +void +ast_iteration_statement::print(void) const +{ + switch (mode) { + case ast_for: + printf("for( "); + if (init_statement) + init_statement->print(); + printf("; "); + + if (condition) + condition->print(); + printf("; "); + + if (rest_expression) + rest_expression->print(); + printf(") "); + + body->print(); + break; + + case ast_while: + printf("while ( "); + if (condition) + condition->print(); + printf(") "); + body->print(); + break; + + case ast_do_while: + printf("do "); + body->print(); + printf("while ( "); + if (condition) + condition->print(); + printf("); "); + break; + } +} + + +ast_iteration_statement::ast_iteration_statement(int mode, + ast_node *init, + ast_node *condition, + ast_expression *rest_expression, + ast_node *body) +{ + this->mode = ast_iteration_modes(mode); + this->init_statement = init; + this->condition = condition; + this->rest_expression = rest_expression; + this->body = body; +} + + +void +ast_struct_specifier::print(void) const +{ + printf("struct %s { ", name); + foreach_list_const(n, &this->declarations) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + printf("} "); +} + + +ast_struct_specifier::ast_struct_specifier(char *identifier, + ast_node *declarator_list) +{ + if (identifier == NULL) { + static unsigned anon_count = 1; + identifier = ralloc_asprintf(this, "#anon_struct_%04x", anon_count); + anon_count++; + } + name = identifier; + this->declarations.push_degenerate_list_at_head(&declarator_list->link); +} + +bool +do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations) +{ + GLboolean progress = GL_FALSE; + + progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress; + + if (linked) { + progress = do_function_inlining(ir) || progress; + progress = do_dead_functions(ir) || progress; + progress = do_structure_splitting(ir) || progress; + } + progress = do_if_simplification(ir) || progress; + progress = do_discard_simplification(ir) || progress; + progress = do_copy_propagation(ir) || progress; + progress = do_copy_propagation_elements(ir) || progress; + if (linked) + progress = do_dead_code(ir) || progress; + else + progress = do_dead_code_unlinked(ir) || progress; + progress = do_dead_code_local(ir) || progress; + progress = do_tree_grafting(ir) || progress; + progress = do_constant_propagation(ir) || progress; + if (linked) + progress = do_constant_variable(ir) || progress; + else + progress = do_constant_variable_unlinked(ir) || progress; + progress = do_constant_folding(ir) || progress; + progress = do_algebraic(ir) || progress; + progress = do_lower_jumps(ir) || progress; + progress = do_vec_index_to_swizzle(ir) || progress; + progress = do_swizzle_swizzle(ir) || progress; + progress = do_noop_swizzle(ir) || progress; + + progress = optimize_redundant_jumps(ir) || progress; + + loop_state *ls = analyze_loop_variables(ir); + if (ls->loop_found) { + progress = set_loop_controls(ir, ls) || progress; + progress = unroll_loops(ir, ls, max_unroll_iterations) || progress; + } + delete ls; + + return progress; +} + +extern "C" { + +/** + * To be called at GL teardown time, this frees compiler datastructures. + * + * After calling this, any previously compiled shaders and shader + * programs would be invalid. So this should happen at approximately + * program exit. + */ +void +_mesa_destroy_shader_compiler(void) +{ + _mesa_destroy_shader_compiler_caches(); + + _mesa_glsl_release_types(); +} + +/** + * Releases compiler caches to trade off performance for memory. + * + * Intended to be used with glReleaseShaderCompiler(). + */ +void +_mesa_destroy_shader_compiler_caches(void) +{ + _mesa_glsl_release_functions(); +} + +} diff --git a/mesalib/src/glsl/glsl_parser_extras.h b/mesalib/src/glsl/glsl_parser_extras.h index 6868b5f10..903e9744d 100644 --- a/mesalib/src/glsl/glsl_parser_extras.h +++ b/mesalib/src/glsl/glsl_parser_extras.h @@ -1,295 +1,295 @@ -/* - * 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. - */ - -#pragma once -#ifndef GLSL_PARSER_EXTRAS_H -#define GLSL_PARSER_EXTRAS_H - -/* - * Most of the definitions here only apply to C++ - */ -#ifdef __cplusplus - - -#include -#include "glsl_symbol_table.h" - -enum _mesa_glsl_parser_targets { - vertex_shader, - geometry_shader, - fragment_shader -}; - -struct gl_context; - -struct _mesa_glsl_parse_state { - _mesa_glsl_parse_state(struct gl_context *ctx, GLenum target, - void *mem_ctx); - - /* Callers of this ralloc-based new need not call delete. It's - * easier to just ralloc_free 'ctx' (or any of its ancestors). */ - static void* operator new(size_t size, void *ctx) - { - void *mem = rzalloc_size(ctx, size); - assert(mem != NULL); - - return mem; - } - - /* If the user *does* call delete, that's OK, we will just - * ralloc_free in that case. */ - static void operator delete(void *mem, void *ctx) - { - ralloc_free(mem); - } - static void operator delete(void *mem) - { - ralloc_free(mem); - } - - void *scanner; - exec_list translation_unit; - glsl_symbol_table *symbols; - - bool es_shader; - unsigned language_version; - const char *version_string; - enum _mesa_glsl_parser_targets target; - - /** - * Printable list of GLSL versions supported by the current context - * - * \note - * This string should probably be generated per-context instead of per - * invokation of the compiler. This should be changed when the method of - * tracking supported GLSL versions changes. - */ - const char *supported_version_string; - - /** - * Implementation defined limits that affect built-in variables, etc. - * - * \sa struct gl_constants (in mtypes.h) - */ - struct { - /* 1.10 */ - unsigned MaxLights; - unsigned MaxClipPlanes; - unsigned MaxTextureUnits; - unsigned MaxTextureCoords; - unsigned MaxVertexAttribs; - unsigned MaxVertexUniformComponents; - unsigned MaxVaryingFloats; - unsigned MaxVertexTextureImageUnits; - unsigned MaxCombinedTextureImageUnits; - unsigned MaxTextureImageUnits; - unsigned MaxFragmentUniformComponents; - - /* ARB_draw_buffers */ - unsigned MaxDrawBuffers; - - /** - * Set of GLSL versions supported by the current context - * - * Knowing that version X is supported doesn't mean that versions before - * X are also supported. Version 1.00 is only supported in an ES2 - * context or when GL_ARB_ES2_compatibility is supported. In an OpenGL - * 3.0 "forward compatible" context, GLSL 1.10 and 1.20 are \b not - * supported. - */ - /*@{*/ - unsigned GLSL_100ES:1; - unsigned GLSL_110:1; - unsigned GLSL_120:1; - unsigned GLSL_130:1; - /*@}*/ - } Const; - - /** - * During AST to IR conversion, pointer to current IR function - * - * Will be \c NULL whenever the AST to IR conversion is not inside a - * function definition. - */ - class ir_function_signature *current_function; - - /** - * During AST to IR conversion, pointer to the toplevel IR - * instruction list being generated. - */ - exec_list *toplevel_ir; - - /** Have we found a return statement in this function? */ - bool found_return; - - /** Was there an error during compilation? */ - bool error; - - /** - * Are all shader inputs / outputs invariant? - * - * This is set when the 'STDGL invariant(all)' pragma is used. - */ - bool all_invariant; - - /** Loop or switch statement containing the current instructions. */ - class ir_instruction *loop_or_switch_nesting; - class ast_iteration_statement *loop_or_switch_nesting_ast; - - /** List of structures defined in user code. */ - const glsl_type **user_structures; - unsigned num_user_structures; - - char *info_log; - - /** - * \name Enable bits for GLSL extensions - */ - /*@{*/ - bool ARB_draw_buffers_enable; - bool ARB_draw_buffers_warn; - bool ARB_draw_instanced_enable; - bool ARB_draw_instanced_warn; - bool ARB_explicit_attrib_location_enable; - bool ARB_explicit_attrib_location_warn; - bool ARB_fragment_coord_conventions_enable; - bool ARB_fragment_coord_conventions_warn; - bool ARB_texture_rectangle_enable; - bool ARB_texture_rectangle_warn; - bool EXT_texture_array_enable; - bool EXT_texture_array_warn; - bool ARB_shader_texture_lod_enable; - bool ARB_shader_texture_lod_warn; - bool ARB_shader_stencil_export_enable; - bool ARB_shader_stencil_export_warn; - bool AMD_conservative_depth_enable; - bool AMD_conservative_depth_warn; - bool ARB_conservative_depth_enable; - bool ARB_conservative_depth_warn; - bool AMD_shader_stencil_export_enable; - bool AMD_shader_stencil_export_warn; - bool OES_texture_3D_enable; - bool OES_texture_3D_warn; - /*@}*/ - - /** Extensions supported by the OpenGL implementation. */ - const struct gl_extensions *extensions; - - /** Shaders containing built-in functions that are used for linking. */ - struct gl_shader *builtins_to_link[16]; - unsigned num_builtins_to_link; -}; - -typedef struct YYLTYPE { - int first_line; - int first_column; - int last_line; - int last_column; - unsigned source; -} YYLTYPE; -# define YYLTYPE_IS_DECLARED 1 -# define YYLTYPE_IS_TRIVIAL 1 - -# define YYLLOC_DEFAULT(Current, Rhs, N) \ -do { \ - if (N) \ - { \ - (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC(Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC(Rhs, 0).last_column; \ - } \ - (Current).source = 0; \ -} while (0) - -extern void _mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...); - -/** - * Emit a warning to the shader log - * - * \sa _mesa_glsl_error - */ -extern void _mesa_glsl_warning(const YYLTYPE *locp, - _mesa_glsl_parse_state *state, - const char *fmt, ...); - -extern void _mesa_glsl_lexer_ctor(struct _mesa_glsl_parse_state *state, - const char *string); - -extern void _mesa_glsl_lexer_dtor(struct _mesa_glsl_parse_state *state); - -union YYSTYPE; -extern int _mesa_glsl_lex(union YYSTYPE *yylval, YYLTYPE *yylloc, - void *scanner); - -extern int _mesa_glsl_parse(struct _mesa_glsl_parse_state *); - -/** - * Process elements of the #extension directive - * - * \return - * If \c name and \c behavior are valid, \c true is returned. Otherwise - * \c false is returned. - */ -extern bool _mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, - const char *behavior, - YYLTYPE *behavior_locp, - _mesa_glsl_parse_state *state); - -/** - * Get the textual name of the specified shader target - */ -extern const char * -_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target); - - -#endif /* __cplusplus */ - - -/* - * These definitions apply to C and C++ - */ -#ifdef __cplusplus -extern "C" { -#endif - -extern int preprocess(void *ctx, const char **shader, char **info_log, - const struct gl_extensions *extensions, int api); - -extern void _mesa_destroy_shader_compiler(void); -extern void _mesa_destroy_shader_compiler_caches(void); - -#ifdef __cplusplus -} -#endif - - -#endif /* GLSL_PARSER_EXTRAS_H */ +/* + * 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. + */ + +#pragma once +#ifndef GLSL_PARSER_EXTRAS_H +#define GLSL_PARSER_EXTRAS_H + +/* + * Most of the definitions here only apply to C++ + */ +#ifdef __cplusplus + + +#include +#include "glsl_symbol_table.h" + +enum _mesa_glsl_parser_targets { + vertex_shader, + geometry_shader, + fragment_shader +}; + +struct gl_context; + +struct _mesa_glsl_parse_state { + _mesa_glsl_parse_state(struct gl_context *ctx, GLenum target, + void *mem_ctx); + + /* Callers of this ralloc-based new need not call delete. It's + * easier to just ralloc_free 'ctx' (or any of its ancestors). */ + static void* operator new(size_t size, void *ctx) + { + void *mem = rzalloc_size(ctx, size); + assert(mem != NULL); + + return mem; + } + + /* If the user *does* call delete, that's OK, we will just + * ralloc_free in that case. */ + static void operator delete(void *mem, void *ctx) + { + ralloc_free(mem); + } + static void operator delete(void *mem) + { + ralloc_free(mem); + } + + void *scanner; + exec_list translation_unit; + glsl_symbol_table *symbols; + + bool es_shader; + unsigned language_version; + const char *version_string; + enum _mesa_glsl_parser_targets target; + + /** + * Printable list of GLSL versions supported by the current context + * + * \note + * This string should probably be generated per-context instead of per + * invokation of the compiler. This should be changed when the method of + * tracking supported GLSL versions changes. + */ + const char *supported_version_string; + + /** + * Implementation defined limits that affect built-in variables, etc. + * + * \sa struct gl_constants (in mtypes.h) + */ + struct { + /* 1.10 */ + unsigned MaxLights; + unsigned MaxClipPlanes; + unsigned MaxTextureUnits; + unsigned MaxTextureCoords; + unsigned MaxVertexAttribs; + unsigned MaxVertexUniformComponents; + unsigned MaxVaryingFloats; + unsigned MaxVertexTextureImageUnits; + unsigned MaxCombinedTextureImageUnits; + unsigned MaxTextureImageUnits; + unsigned MaxFragmentUniformComponents; + + /* ARB_draw_buffers */ + unsigned MaxDrawBuffers; + + /** + * Set of GLSL versions supported by the current context + * + * Knowing that version X is supported doesn't mean that versions before + * X are also supported. Version 1.00 is only supported in an ES2 + * context or when GL_ARB_ES2_compatibility is supported. In an OpenGL + * 3.0 "forward compatible" context, GLSL 1.10 and 1.20 are \b not + * supported. + */ + /*@{*/ + unsigned GLSL_100ES:1; + unsigned GLSL_110:1; + unsigned GLSL_120:1; + unsigned GLSL_130:1; + /*@}*/ + } Const; + + /** + * During AST to IR conversion, pointer to current IR function + * + * Will be \c NULL whenever the AST to IR conversion is not inside a + * function definition. + */ + class ir_function_signature *current_function; + + /** + * During AST to IR conversion, pointer to the toplevel IR + * instruction list being generated. + */ + exec_list *toplevel_ir; + + /** Have we found a return statement in this function? */ + bool found_return; + + /** Was there an error during compilation? */ + bool error; + + /** + * Are all shader inputs / outputs invariant? + * + * This is set when the 'STDGL invariant(all)' pragma is used. + */ + bool all_invariant; + + /** Loop or switch statement containing the current instructions. */ + class ir_instruction *loop_or_switch_nesting; + class ast_iteration_statement *loop_or_switch_nesting_ast; + + /** List of structures defined in user code. */ + const glsl_type **user_structures; + unsigned num_user_structures; + + char *info_log; + + /** + * \name Enable bits for GLSL extensions + */ + /*@{*/ + bool ARB_draw_buffers_enable; + bool ARB_draw_buffers_warn; + bool ARB_draw_instanced_enable; + bool ARB_draw_instanced_warn; + bool ARB_explicit_attrib_location_enable; + bool ARB_explicit_attrib_location_warn; + bool ARB_fragment_coord_conventions_enable; + bool ARB_fragment_coord_conventions_warn; + bool ARB_texture_rectangle_enable; + bool ARB_texture_rectangle_warn; + bool EXT_texture_array_enable; + bool EXT_texture_array_warn; + bool ARB_shader_texture_lod_enable; + bool ARB_shader_texture_lod_warn; + bool ARB_shader_stencil_export_enable; + bool ARB_shader_stencil_export_warn; + bool AMD_conservative_depth_enable; + bool AMD_conservative_depth_warn; + bool ARB_conservative_depth_enable; + bool ARB_conservative_depth_warn; + bool AMD_shader_stencil_export_enable; + bool AMD_shader_stencil_export_warn; + bool OES_texture_3D_enable; + bool OES_texture_3D_warn; + /*@}*/ + + /** Extensions supported by the OpenGL implementation. */ + const struct gl_extensions *extensions; + + /** Shaders containing built-in functions that are used for linking. */ + struct gl_shader *builtins_to_link[16]; + unsigned num_builtins_to_link; +}; + +typedef struct YYLTYPE { + int first_line; + int first_column; + int last_line; + int last_column; + unsigned source; +} YYLTYPE; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 + +# define YYLLOC_DEFAULT(Current, Rhs, N) \ +do { \ + if (N) \ + { \ + (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC(Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC(Rhs, 0).last_column; \ + } \ + (Current).source = 0; \ +} while (0) + +extern void _mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, + const char *fmt, ...); + +/** + * Emit a warning to the shader log + * + * \sa _mesa_glsl_error + */ +extern void _mesa_glsl_warning(const YYLTYPE *locp, + _mesa_glsl_parse_state *state, + const char *fmt, ...); + +extern void _mesa_glsl_lexer_ctor(struct _mesa_glsl_parse_state *state, + const char *string); + +extern void _mesa_glsl_lexer_dtor(struct _mesa_glsl_parse_state *state); + +union YYSTYPE; +extern int _mesa_glsl_lex(union YYSTYPE *yylval, YYLTYPE *yylloc, + void *scanner); + +extern int _mesa_glsl_parse(struct _mesa_glsl_parse_state *); + +/** + * Process elements of the #extension directive + * + * \return + * If \c name and \c behavior are valid, \c true is returned. Otherwise + * \c false is returned. + */ +extern bool _mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, + const char *behavior, + YYLTYPE *behavior_locp, + _mesa_glsl_parse_state *state); + +/** + * Get the textual name of the specified shader target + */ +extern const char * +_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target); + + +#endif /* __cplusplus */ + + +/* + * These definitions apply to C and C++ + */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int preprocess(void *ctx, const char **shader, char **info_log, + const struct gl_extensions *extensions, int api); + +extern void _mesa_destroy_shader_compiler(void); +extern void _mesa_destroy_shader_compiler_caches(void); + +#ifdef __cplusplus +} +#endif + + +#endif /* GLSL_PARSER_EXTRAS_H */ diff --git a/mesalib/src/glsl/glsl_symbol_table.cpp b/mesalib/src/glsl/glsl_symbol_table.cpp index fa6458df5..27a669b65 100644 --- a/mesalib/src/glsl/glsl_symbol_table.cpp +++ b/mesalib/src/glsl/glsl_symbol_table.cpp @@ -1,169 +1,169 @@ -/* -*- c++ -*- */ -/* - * 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_symbol_table.h" - -class symbol_table_entry { -public: - /* Callers of this ralloc-based new need not call delete. It's - * easier to just ralloc_free 'ctx' (or any of its ancestors). */ - static void* operator new(size_t size, void *ctx) - { - void *entry = ralloc_size(ctx, size); - assert(entry != NULL); - return entry; - } - - /* If the user *does* call delete, that's OK, we will just ralloc_free. */ - static void operator delete(void *entry, void *ctx) - { - ralloc_free(entry); - } - static void operator delete(void *entry) - { - ralloc_free(entry); - } - - symbol_table_entry(ir_variable *v) : v(v), f(0), t(0) {} - symbol_table_entry(ir_function *f) : v(0), f(f), t(0) {} - symbol_table_entry(const glsl_type *t) : v(0), f(0), t(t) {} - - ir_variable *v; - ir_function *f; - const glsl_type *t; -}; - -glsl_symbol_table::glsl_symbol_table() -{ - this->language_version = 120; - this->table = _mesa_symbol_table_ctor(); - this->mem_ctx = ralloc_context(NULL); -} - -glsl_symbol_table::~glsl_symbol_table() -{ - _mesa_symbol_table_dtor(table); - ralloc_free(mem_ctx); -} - -void glsl_symbol_table::push_scope() -{ - _mesa_symbol_table_push_scope(table); -} - -void glsl_symbol_table::pop_scope() -{ - _mesa_symbol_table_pop_scope(table); -} - -bool glsl_symbol_table::name_declared_this_scope(const char *name) -{ - return _mesa_symbol_table_symbol_scope(table, -1, name) == 0; -} - -bool glsl_symbol_table::add_variable(ir_variable *v) -{ - if (this->language_version == 110) { - /* In 1.10, functions and variables have separate namespaces. */ - symbol_table_entry *existing = get_entry(v->name); - if (name_declared_this_scope(v->name)) { - /* If there's already an existing function (not a constructor!) in - * the current scope, just update the existing entry to include 'v'. - */ - if (existing->v == NULL && existing->t == NULL) { - existing->v = v; - return true; - } - } else { - /* If not declared at this scope, add a new entry. But if an existing - * entry includes a function, propagate that to this block - otherwise - * the new variable declaration would shadow the function. - */ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); - if (existing != NULL) - entry->f = existing->f; - int added = _mesa_symbol_table_add_symbol(table, -1, v->name, entry); - assert(added == 0); - (void)added; - return true; - } - return false; - } - - /* 1.20+ rules: */ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); - return _mesa_symbol_table_add_symbol(table, -1, v->name, entry) == 0; -} - -bool glsl_symbol_table::add_type(const char *name, const glsl_type *t) -{ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(t); - return _mesa_symbol_table_add_symbol(table, -1, name, entry) == 0; -} - -bool glsl_symbol_table::add_function(ir_function *f) -{ - if (this->language_version == 110 && name_declared_this_scope(f->name)) { - /* In 1.10, functions and variables have separate namespaces. */ - symbol_table_entry *existing = get_entry(f->name); - if ((existing->f == NULL) && (existing->t == NULL)) { - existing->f = f; - return true; - } - } - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); - return _mesa_symbol_table_add_symbol(table, -1, f->name, entry) == 0; -} - -void glsl_symbol_table::add_global_function(ir_function *f) -{ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); - int added = _mesa_symbol_table_add_global_symbol(table, -1, f->name, entry); - assert(added == 0); - (void)added; -} - -ir_variable *glsl_symbol_table::get_variable(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->v : NULL; -} - -const glsl_type *glsl_symbol_table::get_type(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->t : NULL; -} - -ir_function *glsl_symbol_table::get_function(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->f : NULL; -} - -symbol_table_entry *glsl_symbol_table::get_entry(const char *name) -{ - return (symbol_table_entry *) - _mesa_symbol_table_find_symbol(table, -1, name); -} +/* -*- c++ -*- */ +/* + * 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_symbol_table.h" + +class symbol_table_entry { +public: + /* Callers of this ralloc-based new need not call delete. It's + * easier to just ralloc_free 'ctx' (or any of its ancestors). */ + static void* operator new(size_t size, void *ctx) + { + void *entry = ralloc_size(ctx, size); + assert(entry != NULL); + return entry; + } + + /* If the user *does* call delete, that's OK, we will just ralloc_free. */ + static void operator delete(void *entry, void *ctx) + { + ralloc_free(entry); + } + static void operator delete(void *entry) + { + ralloc_free(entry); + } + + symbol_table_entry(ir_variable *v) : v(v), f(0), t(0) {} + symbol_table_entry(ir_function *f) : v(0), f(f), t(0) {} + symbol_table_entry(const glsl_type *t) : v(0), f(0), t(t) {} + + ir_variable *v; + ir_function *f; + const glsl_type *t; +}; + +glsl_symbol_table::glsl_symbol_table() +{ + this->language_version = 120; + this->table = _mesa_symbol_table_ctor(); + this->mem_ctx = ralloc_context(NULL); +} + +glsl_symbol_table::~glsl_symbol_table() +{ + _mesa_symbol_table_dtor(table); + ralloc_free(mem_ctx); +} + +void glsl_symbol_table::push_scope() +{ + _mesa_symbol_table_push_scope(table); +} + +void glsl_symbol_table::pop_scope() +{ + _mesa_symbol_table_pop_scope(table); +} + +bool glsl_symbol_table::name_declared_this_scope(const char *name) +{ + return _mesa_symbol_table_symbol_scope(table, -1, name) == 0; +} + +bool glsl_symbol_table::add_variable(ir_variable *v) +{ + if (this->language_version == 110) { + /* In 1.10, functions and variables have separate namespaces. */ + symbol_table_entry *existing = get_entry(v->name); + if (name_declared_this_scope(v->name)) { + /* If there's already an existing function (not a constructor!) in + * the current scope, just update the existing entry to include 'v'. + */ + if (existing->v == NULL && existing->t == NULL) { + existing->v = v; + return true; + } + } else { + /* If not declared at this scope, add a new entry. But if an existing + * entry includes a function, propagate that to this block - otherwise + * the new variable declaration would shadow the function. + */ + symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); + if (existing != NULL) + entry->f = existing->f; + int added = _mesa_symbol_table_add_symbol(table, -1, v->name, entry); + assert(added == 0); + (void)added; + return true; + } + return false; + } + + /* 1.20+ rules: */ + symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); + return _mesa_symbol_table_add_symbol(table, -1, v->name, entry) == 0; +} + +bool glsl_symbol_table::add_type(const char *name, const glsl_type *t) +{ + symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(t); + return _mesa_symbol_table_add_symbol(table, -1, name, entry) == 0; +} + +bool glsl_symbol_table::add_function(ir_function *f) +{ + if (this->language_version == 110 && name_declared_this_scope(f->name)) { + /* In 1.10, functions and variables have separate namespaces. */ + symbol_table_entry *existing = get_entry(f->name); + if ((existing->f == NULL) && (existing->t == NULL)) { + existing->f = f; + return true; + } + } + symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); + return _mesa_symbol_table_add_symbol(table, -1, f->name, entry) == 0; +} + +void glsl_symbol_table::add_global_function(ir_function *f) +{ + symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); + int added = _mesa_symbol_table_add_global_symbol(table, -1, f->name, entry); + assert(added == 0); + (void)added; +} + +ir_variable *glsl_symbol_table::get_variable(const char *name) +{ + symbol_table_entry *entry = get_entry(name); + return entry != NULL ? entry->v : NULL; +} + +const glsl_type *glsl_symbol_table::get_type(const char *name) +{ + symbol_table_entry *entry = get_entry(name); + return entry != NULL ? entry->t : NULL; +} + +ir_function *glsl_symbol_table::get_function(const char *name) +{ + symbol_table_entry *entry = get_entry(name); + return entry != NULL ? entry->f : NULL; +} + +symbol_table_entry *glsl_symbol_table::get_entry(const char *name) +{ + return (symbol_table_entry *) + _mesa_symbol_table_find_symbol(table, -1, name); +} diff --git a/mesalib/src/glsl/glsl_types.cpp b/mesalib/src/glsl/glsl_types.cpp index 758fcf756..c94aec0d2 100644 --- a/mesalib/src/glsl/glsl_types.cpp +++ b/mesalib/src/glsl/glsl_types.cpp @@ -1,541 +1,541 @@ -/* - * Copyright © 2009 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 -#include -#include "main/core.h" /* for Elements */ -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "glsl_types.h" -#include "builtin_types.h" -extern "C" { -#include "program/hash_table.h" -} - -hash_table *glsl_type::array_types = NULL; -hash_table *glsl_type::record_types = NULL; -void *glsl_type::mem_ctx = NULL; - -void -glsl_type::init_ralloc_type_ctx(void) -{ - if (glsl_type::mem_ctx == NULL) { - glsl_type::mem_ctx = ralloc_autofree_context(); - assert(glsl_type::mem_ctx != NULL); - } -} - -glsl_type::glsl_type(GLenum gl_type, - glsl_base_type base_type, unsigned vector_elements, - unsigned matrix_columns, const char *name) : - gl_type(gl_type), - base_type(base_type), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(vector_elements), matrix_columns(matrix_columns), - length(0) -{ - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - /* Neither dimension is zero or both dimensions are zero. - */ - assert((vector_elements == 0) == (matrix_columns == 0)); - memset(& fields, 0, sizeof(fields)); -} - -glsl_type::glsl_type(GLenum gl_type, - enum glsl_sampler_dim dim, bool shadow, bool array, - unsigned type, const char *name) : - gl_type(gl_type), - base_type(GLSL_TYPE_SAMPLER), - sampler_dimensionality(dim), sampler_shadow(shadow), - sampler_array(array), sampler_type(type), - vector_elements(0), matrix_columns(0), - length(0) -{ - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - memset(& fields, 0, sizeof(fields)); -} - -glsl_type::glsl_type(const glsl_struct_field *fields, unsigned num_fields, - const char *name) : - base_type(GLSL_TYPE_STRUCT), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(0), matrix_columns(0), - length(num_fields) -{ - unsigned int i; - - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - this->fields.structure = ralloc_array(this->mem_ctx, - glsl_struct_field, length); - for (i = 0; i < length; i++) { - this->fields.structure[i].type = fields[i].type; - this->fields.structure[i].name = ralloc_strdup(this->fields.structure, - fields[i].name); - } -} - -static void -add_types_to_symbol_table(glsl_symbol_table *symtab, - const struct glsl_type *types, - unsigned num_types, bool warn) -{ - (void) warn; - - for (unsigned i = 0; i < num_types; i++) { - symtab->add_type(types[i].name, & types[i]); - } -} - -bool -glsl_type::contains_sampler() const -{ - if (this->is_array()) { - return this->fields.array->contains_sampler(); - } else if (this->is_record()) { - for (unsigned int i = 0; i < this->length; i++) { - if (this->fields.structure[i].type->contains_sampler()) - return true; - } - return false; - } else { - return this->is_sampler(); - } -} - -void -glsl_type::generate_100ES_types(glsl_symbol_table *symtab) -{ - add_types_to_symbol_table(symtab, builtin_core_types, - Elements(builtin_core_types), - false); - add_types_to_symbol_table(symtab, builtin_structure_types, - Elements(builtin_structure_types), - false); - add_types_to_symbol_table(symtab, void_type, 1, false); -} - -void -glsl_type::generate_110_types(glsl_symbol_table *symtab) -{ - generate_100ES_types(symtab); - - add_types_to_symbol_table(symtab, builtin_110_types, - Elements(builtin_110_types), - false); - add_types_to_symbol_table(symtab, &_sampler3D_type, 1, false); - add_types_to_symbol_table(symtab, builtin_110_deprecated_structure_types, - Elements(builtin_110_deprecated_structure_types), - false); -} - - -void -glsl_type::generate_120_types(glsl_symbol_table *symtab) -{ - generate_110_types(symtab); - - add_types_to_symbol_table(symtab, builtin_120_types, - Elements(builtin_120_types), false); -} - - -void -glsl_type::generate_130_types(glsl_symbol_table *symtab) -{ - generate_120_types(symtab); - - add_types_to_symbol_table(symtab, builtin_130_types, - Elements(builtin_130_types), false); - generate_EXT_texture_array_types(symtab, false); -} - - -void -glsl_type::generate_ARB_texture_rectangle_types(glsl_symbol_table *symtab, - bool warn) -{ - add_types_to_symbol_table(symtab, builtin_ARB_texture_rectangle_types, - Elements(builtin_ARB_texture_rectangle_types), - warn); -} - - -void -glsl_type::generate_EXT_texture_array_types(glsl_symbol_table *symtab, - bool warn) -{ - add_types_to_symbol_table(symtab, builtin_EXT_texture_array_types, - Elements(builtin_EXT_texture_array_types), - warn); -} - - -void -glsl_type::generate_OES_texture_3D_types(glsl_symbol_table *symtab, bool warn) -{ - add_types_to_symbol_table(symtab, &_sampler3D_type, 1, warn); -} - - -void -_mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state) -{ - switch (state->language_version) { - case 100: - assert(state->es_shader); - glsl_type::generate_100ES_types(state->symbols); - break; - case 110: - glsl_type::generate_110_types(state->symbols); - break; - case 120: - glsl_type::generate_120_types(state->symbols); - break; - case 130: - glsl_type::generate_130_types(state->symbols); - break; - default: - /* error */ - break; - } - - if (state->ARB_texture_rectangle_enable) { - glsl_type::generate_ARB_texture_rectangle_types(state->symbols, - state->ARB_texture_rectangle_warn); - } - if (state->OES_texture_3D_enable && state->language_version == 100) { - glsl_type::generate_OES_texture_3D_types(state->symbols, - state->OES_texture_3D_warn); - } - - if (state->EXT_texture_array_enable && state->language_version < 130) { - // These are already included in 130; don't create twice. - glsl_type::generate_EXT_texture_array_types(state->symbols, - state->EXT_texture_array_warn); - } -} - - -const glsl_type *glsl_type::get_base_type() const -{ - switch (base_type) { - case GLSL_TYPE_UINT: - return uint_type; - case GLSL_TYPE_INT: - return int_type; - case GLSL_TYPE_FLOAT: - return float_type; - case GLSL_TYPE_BOOL: - return bool_type; - default: - return error_type; - } -} - - -void -_mesa_glsl_release_types(void) -{ - if (glsl_type::array_types != NULL) { - hash_table_dtor(glsl_type::array_types); - glsl_type::array_types = NULL; - } - - if (glsl_type::record_types != NULL) { - hash_table_dtor(glsl_type::record_types); - glsl_type::record_types = NULL; - } -} - - -glsl_type::glsl_type(const glsl_type *array, unsigned length) : - base_type(GLSL_TYPE_ARRAY), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(0), matrix_columns(0), - name(NULL), length(length) -{ - this->fields.array = array; - /* Inherit the gl type of the base. The GL type is used for - * uniform/statevar handling in Mesa and the arrayness of the type - * is represented by the size rather than the type. - */ - this->gl_type = array->gl_type; - - /* Allow a maximum of 10 characters for the array size. This is enough - * for 32-bits of ~0. The extra 3 are for the '[', ']', and terminating - * NUL. - */ - const unsigned name_length = strlen(array->name) + 10 + 3; - char *const n = (char *) ralloc_size(this->mem_ctx, name_length); - - if (length == 0) - snprintf(n, name_length, "%s[]", array->name); - else - snprintf(n, name_length, "%s[%u]", array->name, length); - - this->name = n; -} - - -const glsl_type * -glsl_type::get_instance(unsigned base_type, unsigned rows, unsigned columns) -{ - if (base_type == GLSL_TYPE_VOID) - return void_type; - - if ((rows < 1) || (rows > 4) || (columns < 1) || (columns > 4)) - return error_type; - - /* Treat GLSL vectors as Nx1 matrices. - */ - if (columns == 1) { - switch (base_type) { - case GLSL_TYPE_UINT: - return uint_type + (rows - 1); - case GLSL_TYPE_INT: - return int_type + (rows - 1); - case GLSL_TYPE_FLOAT: - return float_type + (rows - 1); - case GLSL_TYPE_BOOL: - return bool_type + (rows - 1); - default: - return error_type; - } - } else { - if ((base_type != GLSL_TYPE_FLOAT) || (rows == 1)) - return error_type; - - /* GLSL matrix types are named mat{COLUMNS}x{ROWS}. Only the following - * combinations are valid: - * - * 1 2 3 4 - * 1 - * 2 x x x - * 3 x x x - * 4 x x x - */ -#define IDX(c,r) (((c-1)*3) + (r-1)) - - switch (IDX(columns, rows)) { - case IDX(2,2): return mat2_type; - case IDX(2,3): return mat2x3_type; - case IDX(2,4): return mat2x4_type; - case IDX(3,2): return mat3x2_type; - case IDX(3,3): return mat3_type; - case IDX(3,4): return mat3x4_type; - case IDX(4,2): return mat4x2_type; - case IDX(4,3): return mat4x3_type; - case IDX(4,4): return mat4_type; - default: return error_type; - } - } - - assert(!"Should not get here."); - return error_type; -} - - -const glsl_type * -glsl_type::get_array_instance(const glsl_type *base, unsigned array_size) -{ - - if (array_types == NULL) { - array_types = hash_table_ctor(64, hash_table_string_hash, - hash_table_string_compare); - } - - /* Generate a name using the base type pointer in the key. This is - * done because the name of the base type may not be unique across - * shaders. For example, two shaders may have different record types - * named 'foo'. - */ - char key[128]; - snprintf(key, sizeof(key), "%p[%u]", (void *) base, array_size); - - const glsl_type *t = (glsl_type *) hash_table_find(array_types, key); - if (t == NULL) { - t = new glsl_type(base, array_size); - - hash_table_insert(array_types, (void *) t, ralloc_strdup(mem_ctx, key)); - } - - assert(t->base_type == GLSL_TYPE_ARRAY); - assert(t->length == array_size); - assert(t->fields.array == base); - - return t; -} - - -int -glsl_type::record_key_compare(const void *a, const void *b) -{ - const glsl_type *const key1 = (glsl_type *) a; - const glsl_type *const key2 = (glsl_type *) b; - - /* Return zero is the types match (there is zero difference) or non-zero - * otherwise. - */ - if (strcmp(key1->name, key2->name) != 0) - return 1; - - if (key1->length != key2->length) - return 1; - - for (unsigned i = 0; i < key1->length; i++) { - if (key1->fields.structure[i].type != key2->fields.structure[i].type) - return 1; - if (strcmp(key1->fields.structure[i].name, - key2->fields.structure[i].name) != 0) - return 1; - } - - return 0; -} - - -unsigned -glsl_type::record_key_hash(const void *a) -{ - const glsl_type *const key = (glsl_type *) a; - char hash_key[128]; - unsigned size = 0; - - size = snprintf(hash_key, sizeof(hash_key), "%08x", key->length); - - for (unsigned i = 0; i < key->length; i++) { - if (size >= sizeof(hash_key)) - break; - - size += snprintf(& hash_key[size], sizeof(hash_key) - size, - "%p", (void *) key->fields.structure[i].type); - } - - return hash_table_string_hash(& hash_key); -} - - -const glsl_type * -glsl_type::get_record_instance(const glsl_struct_field *fields, - unsigned num_fields, - const char *name) -{ - const glsl_type key(fields, num_fields, name); - - if (record_types == NULL) { - record_types = hash_table_ctor(64, record_key_hash, record_key_compare); - } - - const glsl_type *t = (glsl_type *) hash_table_find(record_types, & key); - if (t == NULL) { - t = new glsl_type(fields, num_fields, name); - - hash_table_insert(record_types, (void *) t, t); - } - - assert(t->base_type == GLSL_TYPE_STRUCT); - assert(t->length == num_fields); - assert(strcmp(t->name, name) == 0); - - return t; -} - - -const glsl_type * -glsl_type::field_type(const char *name) const -{ - if (this->base_type != GLSL_TYPE_STRUCT) - return error_type; - - for (unsigned i = 0; i < this->length; i++) { - if (strcmp(name, this->fields.structure[i].name) == 0) - return this->fields.structure[i].type; - } - - return error_type; -} - - -int -glsl_type::field_index(const char *name) const -{ - if (this->base_type != GLSL_TYPE_STRUCT) - return -1; - - for (unsigned i = 0; i < this->length; i++) { - if (strcmp(name, this->fields.structure[i].name) == 0) - return i; - } - - return -1; -} - - -unsigned -glsl_type::component_slots() const -{ - switch (this->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_BOOL: - return this->components(); - - case GLSL_TYPE_STRUCT: { - unsigned size = 0; - - for (unsigned i = 0; i < this->length; i++) - size += this->fields.structure[i].type->component_slots(); - - return size; - } - - case GLSL_TYPE_ARRAY: - return this->length * this->fields.array->component_slots(); - - default: - return 0; - } -} - -bool -glsl_type::can_implicitly_convert_to(const glsl_type *desired) const -{ - if (this == desired) - return true; - - /* There is no conversion among matrix types. */ - if (this->matrix_columns > 1 || desired->matrix_columns > 1) - return false; - - /* int and uint can be converted to float. */ - return desired->is_float() - && this->is_integer() - && this->vector_elements == desired->vector_elements; -} +/* + * Copyright © 2009 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 +#include +#include "main/core.h" /* for Elements */ +#include "glsl_symbol_table.h" +#include "glsl_parser_extras.h" +#include "glsl_types.h" +#include "builtin_types.h" +extern "C" { +#include "program/hash_table.h" +} + +hash_table *glsl_type::array_types = NULL; +hash_table *glsl_type::record_types = NULL; +void *glsl_type::mem_ctx = NULL; + +void +glsl_type::init_ralloc_type_ctx(void) +{ + if (glsl_type::mem_ctx == NULL) { + glsl_type::mem_ctx = ralloc_autofree_context(); + assert(glsl_type::mem_ctx != NULL); + } +} + +glsl_type::glsl_type(GLenum gl_type, + glsl_base_type base_type, unsigned vector_elements, + unsigned matrix_columns, const char *name) : + gl_type(gl_type), + base_type(base_type), + sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), + sampler_type(0), + vector_elements(vector_elements), matrix_columns(matrix_columns), + length(0) +{ + init_ralloc_type_ctx(); + this->name = ralloc_strdup(this->mem_ctx, name); + /* Neither dimension is zero or both dimensions are zero. + */ + assert((vector_elements == 0) == (matrix_columns == 0)); + memset(& fields, 0, sizeof(fields)); +} + +glsl_type::glsl_type(GLenum gl_type, + enum glsl_sampler_dim dim, bool shadow, bool array, + unsigned type, const char *name) : + gl_type(gl_type), + base_type(GLSL_TYPE_SAMPLER), + sampler_dimensionality(dim), sampler_shadow(shadow), + sampler_array(array), sampler_type(type), + vector_elements(0), matrix_columns(0), + length(0) +{ + init_ralloc_type_ctx(); + this->name = ralloc_strdup(this->mem_ctx, name); + memset(& fields, 0, sizeof(fields)); +} + +glsl_type::glsl_type(const glsl_struct_field *fields, unsigned num_fields, + const char *name) : + base_type(GLSL_TYPE_STRUCT), + sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), + sampler_type(0), + vector_elements(0), matrix_columns(0), + length(num_fields) +{ + unsigned int i; + + init_ralloc_type_ctx(); + this->name = ralloc_strdup(this->mem_ctx, name); + this->fields.structure = ralloc_array(this->mem_ctx, + glsl_struct_field, length); + for (i = 0; i < length; i++) { + this->fields.structure[i].type = fields[i].type; + this->fields.structure[i].name = ralloc_strdup(this->fields.structure, + fields[i].name); + } +} + +static void +add_types_to_symbol_table(glsl_symbol_table *symtab, + const struct glsl_type *types, + unsigned num_types, bool warn) +{ + (void) warn; + + for (unsigned i = 0; i < num_types; i++) { + symtab->add_type(types[i].name, & types[i]); + } +} + +bool +glsl_type::contains_sampler() const +{ + if (this->is_array()) { + return this->fields.array->contains_sampler(); + } else if (this->is_record()) { + for (unsigned int i = 0; i < this->length; i++) { + if (this->fields.structure[i].type->contains_sampler()) + return true; + } + return false; + } else { + return this->is_sampler(); + } +} + +void +glsl_type::generate_100ES_types(glsl_symbol_table *symtab) +{ + add_types_to_symbol_table(symtab, builtin_core_types, + Elements(builtin_core_types), + false); + add_types_to_symbol_table(symtab, builtin_structure_types, + Elements(builtin_structure_types), + false); + add_types_to_symbol_table(symtab, void_type, 1, false); +} + +void +glsl_type::generate_110_types(glsl_symbol_table *symtab) +{ + generate_100ES_types(symtab); + + add_types_to_symbol_table(symtab, builtin_110_types, + Elements(builtin_110_types), + false); + add_types_to_symbol_table(symtab, &_sampler3D_type, 1, false); + add_types_to_symbol_table(symtab, builtin_110_deprecated_structure_types, + Elements(builtin_110_deprecated_structure_types), + false); +} + + +void +glsl_type::generate_120_types(glsl_symbol_table *symtab) +{ + generate_110_types(symtab); + + add_types_to_symbol_table(symtab, builtin_120_types, + Elements(builtin_120_types), false); +} + + +void +glsl_type::generate_130_types(glsl_symbol_table *symtab) +{ + generate_120_types(symtab); + + add_types_to_symbol_table(symtab, builtin_130_types, + Elements(builtin_130_types), false); + generate_EXT_texture_array_types(symtab, false); +} + + +void +glsl_type::generate_ARB_texture_rectangle_types(glsl_symbol_table *symtab, + bool warn) +{ + add_types_to_symbol_table(symtab, builtin_ARB_texture_rectangle_types, + Elements(builtin_ARB_texture_rectangle_types), + warn); +} + + +void +glsl_type::generate_EXT_texture_array_types(glsl_symbol_table *symtab, + bool warn) +{ + add_types_to_symbol_table(symtab, builtin_EXT_texture_array_types, + Elements(builtin_EXT_texture_array_types), + warn); +} + + +void +glsl_type::generate_OES_texture_3D_types(glsl_symbol_table *symtab, bool warn) +{ + add_types_to_symbol_table(symtab, &_sampler3D_type, 1, warn); +} + + +void +_mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state) +{ + switch (state->language_version) { + case 100: + assert(state->es_shader); + glsl_type::generate_100ES_types(state->symbols); + break; + case 110: + glsl_type::generate_110_types(state->symbols); + break; + case 120: + glsl_type::generate_120_types(state->symbols); + break; + case 130: + glsl_type::generate_130_types(state->symbols); + break; + default: + /* error */ + break; + } + + if (state->ARB_texture_rectangle_enable) { + glsl_type::generate_ARB_texture_rectangle_types(state->symbols, + state->ARB_texture_rectangle_warn); + } + if (state->OES_texture_3D_enable && state->language_version == 100) { + glsl_type::generate_OES_texture_3D_types(state->symbols, + state->OES_texture_3D_warn); + } + + if (state->EXT_texture_array_enable && state->language_version < 130) { + // These are already included in 130; don't create twice. + glsl_type::generate_EXT_texture_array_types(state->symbols, + state->EXT_texture_array_warn); + } +} + + +const glsl_type *glsl_type::get_base_type() const +{ + switch (base_type) { + case GLSL_TYPE_UINT: + return uint_type; + case GLSL_TYPE_INT: + return int_type; + case GLSL_TYPE_FLOAT: + return float_type; + case GLSL_TYPE_BOOL: + return bool_type; + default: + return error_type; + } +} + + +void +_mesa_glsl_release_types(void) +{ + if (glsl_type::array_types != NULL) { + hash_table_dtor(glsl_type::array_types); + glsl_type::array_types = NULL; + } + + if (glsl_type::record_types != NULL) { + hash_table_dtor(glsl_type::record_types); + glsl_type::record_types = NULL; + } +} + + +glsl_type::glsl_type(const glsl_type *array, unsigned length) : + base_type(GLSL_TYPE_ARRAY), + sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), + sampler_type(0), + vector_elements(0), matrix_columns(0), + name(NULL), length(length) +{ + this->fields.array = array; + /* Inherit the gl type of the base. The GL type is used for + * uniform/statevar handling in Mesa and the arrayness of the type + * is represented by the size rather than the type. + */ + this->gl_type = array->gl_type; + + /* Allow a maximum of 10 characters for the array size. This is enough + * for 32-bits of ~0. The extra 3 are for the '[', ']', and terminating + * NUL. + */ + const unsigned name_length = strlen(array->name) + 10 + 3; + char *const n = (char *) ralloc_size(this->mem_ctx, name_length); + + if (length == 0) + snprintf(n, name_length, "%s[]", array->name); + else + snprintf(n, name_length, "%s[%u]", array->name, length); + + this->name = n; +} + + +const glsl_type * +glsl_type::get_instance(unsigned base_type, unsigned rows, unsigned columns) +{ + if (base_type == GLSL_TYPE_VOID) + return void_type; + + if ((rows < 1) || (rows > 4) || (columns < 1) || (columns > 4)) + return error_type; + + /* Treat GLSL vectors as Nx1 matrices. + */ + if (columns == 1) { + switch (base_type) { + case GLSL_TYPE_UINT: + return uint_type + (rows - 1); + case GLSL_TYPE_INT: + return int_type + (rows - 1); + case GLSL_TYPE_FLOAT: + return float_type + (rows - 1); + case GLSL_TYPE_BOOL: + return bool_type + (rows - 1); + default: + return error_type; + } + } else { + if ((base_type != GLSL_TYPE_FLOAT) || (rows == 1)) + return error_type; + + /* GLSL matrix types are named mat{COLUMNS}x{ROWS}. Only the following + * combinations are valid: + * + * 1 2 3 4 + * 1 + * 2 x x x + * 3 x x x + * 4 x x x + */ +#define IDX(c,r) (((c-1)*3) + (r-1)) + + switch (IDX(columns, rows)) { + case IDX(2,2): return mat2_type; + case IDX(2,3): return mat2x3_type; + case IDX(2,4): return mat2x4_type; + case IDX(3,2): return mat3x2_type; + case IDX(3,3): return mat3_type; + case IDX(3,4): return mat3x4_type; + case IDX(4,2): return mat4x2_type; + case IDX(4,3): return mat4x3_type; + case IDX(4,4): return mat4_type; + default: return error_type; + } + } + + assert(!"Should not get here."); + return error_type; +} + + +const glsl_type * +glsl_type::get_array_instance(const glsl_type *base, unsigned array_size) +{ + + if (array_types == NULL) { + array_types = hash_table_ctor(64, hash_table_string_hash, + hash_table_string_compare); + } + + /* Generate a name using the base type pointer in the key. This is + * done because the name of the base type may not be unique across + * shaders. For example, two shaders may have different record types + * named 'foo'. + */ + char key[128]; + snprintf(key, sizeof(key), "%p[%u]", (void *) base, array_size); + + const glsl_type *t = (glsl_type *) hash_table_find(array_types, key); + if (t == NULL) { + t = new glsl_type(base, array_size); + + hash_table_insert(array_types, (void *) t, ralloc_strdup(mem_ctx, key)); + } + + assert(t->base_type == GLSL_TYPE_ARRAY); + assert(t->length == array_size); + assert(t->fields.array == base); + + return t; +} + + +int +glsl_type::record_key_compare(const void *a, const void *b) +{ + const glsl_type *const key1 = (glsl_type *) a; + const glsl_type *const key2 = (glsl_type *) b; + + /* Return zero is the types match (there is zero difference) or non-zero + * otherwise. + */ + if (strcmp(key1->name, key2->name) != 0) + return 1; + + if (key1->length != key2->length) + return 1; + + for (unsigned i = 0; i < key1->length; i++) { + if (key1->fields.structure[i].type != key2->fields.structure[i].type) + return 1; + if (strcmp(key1->fields.structure[i].name, + key2->fields.structure[i].name) != 0) + return 1; + } + + return 0; +} + + +unsigned +glsl_type::record_key_hash(const void *a) +{ + const glsl_type *const key = (glsl_type *) a; + char hash_key[128]; + unsigned size = 0; + + size = snprintf(hash_key, sizeof(hash_key), "%08x", key->length); + + for (unsigned i = 0; i < key->length; i++) { + if (size >= sizeof(hash_key)) + break; + + size += snprintf(& hash_key[size], sizeof(hash_key) - size, + "%p", (void *) key->fields.structure[i].type); + } + + return hash_table_string_hash(& hash_key); +} + + +const glsl_type * +glsl_type::get_record_instance(const glsl_struct_field *fields, + unsigned num_fields, + const char *name) +{ + const glsl_type key(fields, num_fields, name); + + if (record_types == NULL) { + record_types = hash_table_ctor(64, record_key_hash, record_key_compare); + } + + const glsl_type *t = (glsl_type *) hash_table_find(record_types, & key); + if (t == NULL) { + t = new glsl_type(fields, num_fields, name); + + hash_table_insert(record_types, (void *) t, t); + } + + assert(t->base_type == GLSL_TYPE_STRUCT); + assert(t->length == num_fields); + assert(strcmp(t->name, name) == 0); + + return t; +} + + +const glsl_type * +glsl_type::field_type(const char *name) const +{ + if (this->base_type != GLSL_TYPE_STRUCT) + return error_type; + + for (unsigned i = 0; i < this->length; i++) { + if (strcmp(name, this->fields.structure[i].name) == 0) + return this->fields.structure[i].type; + } + + return error_type; +} + + +int +glsl_type::field_index(const char *name) const +{ + if (this->base_type != GLSL_TYPE_STRUCT) + return -1; + + for (unsigned i = 0; i < this->length; i++) { + if (strcmp(name, this->fields.structure[i].name) == 0) + return i; + } + + return -1; +} + + +unsigned +glsl_type::component_slots() const +{ + switch (this->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + return this->components(); + + case GLSL_TYPE_STRUCT: { + unsigned size = 0; + + for (unsigned i = 0; i < this->length; i++) + size += this->fields.structure[i].type->component_slots(); + + return size; + } + + case GLSL_TYPE_ARRAY: + return this->length * this->fields.array->component_slots(); + + default: + return 0; + } +} + +bool +glsl_type::can_implicitly_convert_to(const glsl_type *desired) const +{ + if (this == desired) + return true; + + /* There is no conversion among matrix types. */ + if (this->matrix_columns > 1 || desired->matrix_columns > 1) + return false; + + /* int and uint can be converted to float. */ + return desired->is_float() + && this->is_integer() + && this->vector_elements == desired->vector_elements; +} diff --git a/mesalib/src/glsl/hir_field_selection.cpp b/mesalib/src/glsl/hir_field_selection.cpp index 21f107405..3c33127b5 100644 --- a/mesalib/src/glsl/hir_field_selection.cpp +++ b/mesalib/src/glsl/hir_field_selection.cpp @@ -1,102 +1,102 @@ -/* - * 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 "ir.h" -#include "program/symbol_table.h" -#include "glsl_parser_extras.h" -#include "ast.h" -#include "glsl_types.h" - -ir_rvalue * -_mesa_ast_field_selection_to_hir(const ast_expression *expr, - exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - ir_rvalue *result = NULL; - ir_rvalue *op; - - op = expr->subexpressions[0]->hir(instructions, state); - - /* There are two kinds of field selection. There is the selection of a - * specific field from a structure, and there is the selection of a - * swizzle / mask from a vector. Which is which is determined entirely - * by the base type of the thing to which the field selection operator is - * being applied. - */ - YYLTYPE loc = expr->get_location(); - if (op->type->is_error()) { - /* silently propagate the error */ - } else if (op->type->is_vector()) { - ir_swizzle *swiz = ir_swizzle::create(op, - expr->primary_expression.identifier, - op->type->vector_elements); - if (swiz != NULL) { - result = swiz; - } else { - /* FINISHME: Logging of error messages should be moved into - * FINISHME: ir_swizzle::create. This allows the generation of more - * FINISHME: specific error messages. - */ - _mesa_glsl_error(& loc, state, "Invalid swizzle / mask `%s'", - expr->primary_expression.identifier); - } - } else if (op->type->base_type == GLSL_TYPE_STRUCT) { - result = new(ctx) ir_dereference_record(op, - expr->primary_expression.identifier); - - if (result->type->is_error()) { - _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " - "structure", - expr->primary_expression.identifier); - } - } else if (expr->subexpressions[1] != NULL) { - /* Handle "method calls" in GLSL 1.20 - namely, array.length() */ - if (state->language_version < 120) - _mesa_glsl_error(&loc, state, "Methods not supported in GLSL 1.10."); - - ast_expression *call = expr->subexpressions[1]; - assert(call->oper == ast_function_call); - - const char *method; - method = call->subexpressions[0]->primary_expression.identifier; - - if (op->type->is_array() && strcmp(method, "length") == 0) { - if (!call->expressions.is_empty()) - _mesa_glsl_error(&loc, state, "length method takes no arguments."); - - if (op->type->array_size() == 0) - _mesa_glsl_error(&loc, state, "length called on unsized array."); - - result = new(ctx) ir_constant(op->type->array_size()); - } else { - _mesa_glsl_error(&loc, state, "Unknown method: `%s'.", method); - } - } else { - _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " - "non-structure / non-vector.", - expr->primary_expression.identifier); - } - - return result ? result : ir_call::get_error_instruction(ctx); -} +/* + * 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 "ir.h" +#include "program/symbol_table.h" +#include "glsl_parser_extras.h" +#include "ast.h" +#include "glsl_types.h" + +ir_rvalue * +_mesa_ast_field_selection_to_hir(const ast_expression *expr, + exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + void *ctx = state; + ir_rvalue *result = NULL; + ir_rvalue *op; + + op = expr->subexpressions[0]->hir(instructions, state); + + /* There are two kinds of field selection. There is the selection of a + * specific field from a structure, and there is the selection of a + * swizzle / mask from a vector. Which is which is determined entirely + * by the base type of the thing to which the field selection operator is + * being applied. + */ + YYLTYPE loc = expr->get_location(); + if (op->type->is_error()) { + /* silently propagate the error */ + } else if (op->type->is_vector()) { + ir_swizzle *swiz = ir_swizzle::create(op, + expr->primary_expression.identifier, + op->type->vector_elements); + if (swiz != NULL) { + result = swiz; + } else { + /* FINISHME: Logging of error messages should be moved into + * FINISHME: ir_swizzle::create. This allows the generation of more + * FINISHME: specific error messages. + */ + _mesa_glsl_error(& loc, state, "Invalid swizzle / mask `%s'", + expr->primary_expression.identifier); + } + } else if (op->type->base_type == GLSL_TYPE_STRUCT) { + result = new(ctx) ir_dereference_record(op, + expr->primary_expression.identifier); + + if (result->type->is_error()) { + _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " + "structure", + expr->primary_expression.identifier); + } + } else if (expr->subexpressions[1] != NULL) { + /* Handle "method calls" in GLSL 1.20 - namely, array.length() */ + if (state->language_version < 120) + _mesa_glsl_error(&loc, state, "Methods not supported in GLSL 1.10."); + + ast_expression *call = expr->subexpressions[1]; + assert(call->oper == ast_function_call); + + const char *method; + method = call->subexpressions[0]->primary_expression.identifier; + + if (op->type->is_array() && strcmp(method, "length") == 0) { + if (!call->expressions.is_empty()) + _mesa_glsl_error(&loc, state, "length method takes no arguments."); + + if (op->type->array_size() == 0) + _mesa_glsl_error(&loc, state, "length called on unsized array."); + + result = new(ctx) ir_constant(op->type->array_size()); + } else { + _mesa_glsl_error(&loc, state, "Unknown method: `%s'.", method); + } + } else { + _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " + "non-structure / non-vector.", + expr->primary_expression.identifier); + } + + return result ? result : ir_call::get_error_instruction(ctx); +} diff --git a/mesalib/src/glsl/ir.cpp b/mesalib/src/glsl/ir.cpp index 7b15c2b84..41ed4f114 100644 --- a/mesalib/src/glsl/ir.cpp +++ b/mesalib/src/glsl/ir.cpp @@ -1,1569 +1,1569 @@ -/* - * 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 -#include "main/core.h" /* for MAX2 */ -#include "ir.h" -#include "ir_visitor.h" -#include "glsl_types.h" - -ir_rvalue::ir_rvalue() -{ - this->type = glsl_type::error_type; -} - -bool ir_rvalue::is_zero() const -{ - return false; -} - -bool ir_rvalue::is_one() const -{ - return false; -} - -bool ir_rvalue::is_negative_one() const -{ - return false; -} - -/** - * Modify the swizzle make to move one component to another - * - * \param m IR swizzle to be modified - * \param from Component in the RHS that is to be swizzled - * \param to Desired swizzle location of \c from - */ -static void -update_rhs_swizzle(ir_swizzle_mask &m, unsigned from, unsigned to) -{ - switch (to) { - case 0: m.x = from; break; - case 1: m.y = from; break; - case 2: m.z = from; break; - case 3: m.w = from; break; - default: assert(!"Should not get here."); - } - - m.num_components = MAX2(m.num_components, (to + 1)); -} - -void -ir_assignment::set_lhs(ir_rvalue *lhs) -{ - void *mem_ctx = this; - bool swizzled = false; - - while (lhs != NULL) { - ir_swizzle *swiz = lhs->as_swizzle(); - - if (swiz == NULL) - break; - - unsigned write_mask = 0; - ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 }; - - for (unsigned i = 0; i < swiz->mask.num_components; i++) { - unsigned c = 0; - - switch (i) { - case 0: c = swiz->mask.x; break; - case 1: c = swiz->mask.y; break; - case 2: c = swiz->mask.z; break; - case 3: c = swiz->mask.w; break; - default: assert(!"Should not get here."); - } - - write_mask |= (((this->write_mask >> i) & 1) << c); - update_rhs_swizzle(rhs_swiz, i, c); - } - - this->write_mask = write_mask; - lhs = swiz->val; - - this->rhs = new(mem_ctx) ir_swizzle(this->rhs, rhs_swiz); - swizzled = true; - } - - if (swizzled) { - /* Now, RHS channels line up with the LHS writemask. Collapse it - * to just the channels that will be written. - */ - ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 }; - int rhs_chan = 0; - for (int i = 0; i < 4; i++) { - if (write_mask & (1 << i)) - update_rhs_swizzle(rhs_swiz, i, rhs_chan++); - } - this->rhs = new(mem_ctx) ir_swizzle(this->rhs, rhs_swiz); - } - - assert((lhs == NULL) || lhs->as_dereference()); - - this->lhs = (ir_dereference *) lhs; -} - -ir_variable * -ir_assignment::whole_variable_written() -{ - ir_variable *v = this->lhs->whole_variable_referenced(); - - if (v == NULL) - return NULL; - - if (v->type->is_scalar()) - return v; - - if (v->type->is_vector()) { - const unsigned mask = (1U << v->type->vector_elements) - 1; - - if (mask != this->write_mask) - return NULL; - } - - /* Either all the vector components are assigned or the variable is some - * composite type (and the whole thing is assigned. - */ - return v; -} - -ir_assignment::ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, - ir_rvalue *condition, unsigned write_mask) -{ - this->ir_type = ir_type_assignment; - this->condition = condition; - this->rhs = rhs; - this->lhs = lhs; - this->write_mask = write_mask; - - if (lhs->type->is_scalar() || lhs->type->is_vector()) { - int lhs_components = 0; - for (int i = 0; i < 4; i++) { - if (write_mask & (1 << i)) - lhs_components++; - } - - assert(lhs_components == this->rhs->type->vector_elements); - } -} - -ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, - ir_rvalue *condition) -{ - this->ir_type = ir_type_assignment; - this->condition = condition; - this->rhs = rhs; - - /* If the RHS is a vector type, assume that all components of the vector - * type are being written to the LHS. The write mask comes from the RHS - * because we can have a case where the LHS is a vec4 and the RHS is a - * vec3. In that case, the assignment is: - * - * (assign (...) (xyz) (var_ref lhs) (var_ref rhs)) - */ - if (rhs->type->is_vector()) - this->write_mask = (1U << rhs->type->vector_elements) - 1; - else if (rhs->type->is_scalar()) - this->write_mask = 1; - else - this->write_mask = 0; - - this->set_lhs(lhs); -} - - -ir_expression::ir_expression(int op, const struct glsl_type *type, - ir_rvalue *op0) -{ - assert(get_num_operands(ir_expression_operation(op)) == 1); - this->ir_type = ir_type_expression; - this->type = type; - this->operation = ir_expression_operation(op); - this->operands[0] = op0; - this->operands[1] = NULL; - this->operands[2] = NULL; - this->operands[3] = NULL; -} - -ir_expression::ir_expression(int op, const struct glsl_type *type, - ir_rvalue *op0, ir_rvalue *op1) -{ - assert(((op1 == NULL) && (get_num_operands(ir_expression_operation(op)) == 1)) - || (get_num_operands(ir_expression_operation(op)) == 2)); - this->ir_type = ir_type_expression; - this->type = type; - this->operation = ir_expression_operation(op); - this->operands[0] = op0; - this->operands[1] = op1; - this->operands[2] = NULL; - this->operands[3] = NULL; -} - -ir_expression::ir_expression(int op, const struct glsl_type *type, - ir_rvalue *op0, ir_rvalue *op1, - ir_rvalue *op2, ir_rvalue *op3) -{ - this->ir_type = ir_type_expression; - this->type = type; - this->operation = ir_expression_operation(op); - this->operands[0] = op0; - this->operands[1] = op1; - this->operands[2] = op2; - this->operands[3] = op3; -} - -ir_expression::ir_expression(int op, ir_rvalue *op0) -{ - this->ir_type = ir_type_expression; - - this->operation = ir_expression_operation(op); - this->operands[0] = op0; - this->operands[1] = NULL; - this->operands[2] = NULL; - this->operands[3] = NULL; - - assert(op <= ir_last_unop); - - switch (this->operation) { - case ir_unop_bit_not: - case ir_unop_logic_not: - case ir_unop_neg: - case ir_unop_abs: - case ir_unop_sign: - case ir_unop_rcp: - case ir_unop_rsq: - case ir_unop_sqrt: - case ir_unop_exp: - case ir_unop_log: - case ir_unop_exp2: - case ir_unop_log2: - case ir_unop_trunc: - case ir_unop_ceil: - case ir_unop_floor: - case ir_unop_fract: - case ir_unop_round_even: - case ir_unop_sin: - case ir_unop_cos: - case ir_unop_sin_reduced: - case ir_unop_cos_reduced: - case ir_unop_dFdx: - case ir_unop_dFdy: - this->type = op0->type; - break; - - case ir_unop_f2i: - case ir_unop_b2i: - case ir_unop_u2i: - this->type = glsl_type::get_instance(GLSL_TYPE_INT, - op0->type->vector_elements, 1); - break; - - case ir_unop_b2f: - case ir_unop_i2f: - case ir_unop_u2f: - this->type = glsl_type::get_instance(GLSL_TYPE_FLOAT, - op0->type->vector_elements, 1); - break; - - case ir_unop_f2b: - case ir_unop_i2b: - this->type = glsl_type::get_instance(GLSL_TYPE_BOOL, - op0->type->vector_elements, 1); - break; - - case ir_unop_i2u: - this->type = glsl_type::get_instance(GLSL_TYPE_UINT, - op0->type->vector_elements, 1); - break; - - case ir_unop_noise: - this->type = glsl_type::float_type; - break; - - case ir_unop_any: - this->type = glsl_type::bool_type; - break; - - default: - assert(!"not reached: missing automatic type setup for ir_expression"); - this->type = op0->type; - break; - } -} - -ir_expression::ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1) -{ - this->ir_type = ir_type_expression; - - this->operation = ir_expression_operation(op); - this->operands[0] = op0; - this->operands[1] = op1; - this->operands[2] = NULL; - this->operands[3] = NULL; - - assert(op > ir_last_unop); - - switch (this->operation) { - case ir_binop_all_equal: - case ir_binop_any_nequal: - this->type = glsl_type::bool_type; - break; - - case ir_binop_add: - case ir_binop_sub: - case ir_binop_min: - case ir_binop_max: - case ir_binop_pow: - case ir_binop_mul: - case ir_binop_div: - case ir_binop_mod: - if (op0->type->is_scalar()) { - this->type = op1->type; - } else if (op1->type->is_scalar()) { - this->type = op0->type; - } else { - /* FINISHME: matrix types */ - assert(!op0->type->is_matrix() && !op1->type->is_matrix()); - assert(op0->type == op1->type); - this->type = op0->type; - } - break; - - case ir_binop_logic_and: - case ir_binop_logic_xor: - case ir_binop_logic_or: - case ir_binop_bit_and: - case ir_binop_bit_xor: - case ir_binop_bit_or: - if (op0->type->is_scalar()) { - this->type = op1->type; - } else if (op1->type->is_scalar()) { - this->type = op0->type; - } - break; - - case ir_binop_equal: - case ir_binop_nequal: - case ir_binop_lequal: - case ir_binop_gequal: - case ir_binop_less: - case ir_binop_greater: - assert(op0->type == op1->type); - this->type = glsl_type::get_instance(GLSL_TYPE_BOOL, - op0->type->vector_elements, 1); - break; - - case ir_binop_dot: - this->type = glsl_type::float_type; - break; - - case ir_binop_lshift: - case ir_binop_rshift: - this->type = op0->type; - break; - - default: - assert(!"not reached: missing automatic type setup for ir_expression"); - this->type = glsl_type::float_type; - } -} - -unsigned int -ir_expression::get_num_operands(ir_expression_operation op) -{ - assert(op <= ir_last_opcode); - - if (op <= ir_last_unop) - return 1; - - if (op <= ir_last_binop) - return 2; - - if (op == ir_quadop_vector) - return 4; - - assert(false); - return 0; -} - -static const char *const operator_strs[] = { - "~", - "!", - "neg", - "abs", - "sign", - "rcp", - "rsq", - "sqrt", - "exp", - "log", - "exp2", - "log2", - "f2i", - "i2f", - "f2b", - "b2f", - "i2b", - "b2i", - "u2f", - "i2u", - "u2i", - "any", - "trunc", - "ceil", - "floor", - "fract", - "round_even", - "sin", - "cos", - "sin_reduced", - "cos_reduced", - "dFdx", - "dFdy", - "noise", - "+", - "-", - "*", - "/", - "%", - "<", - ">", - "<=", - ">=", - "==", - "!=", - "all_equal", - "any_nequal", - "<<", - ">>", - "&", - "^", - "|", - "&&", - "^^", - "||", - "dot", - "min", - "max", - "pow", - "vector", -}; - -const char *ir_expression::operator_string(ir_expression_operation op) -{ - assert((unsigned int) op < Elements(operator_strs)); - assert(Elements(operator_strs) == (ir_quadop_vector + 1)); - return operator_strs[op]; -} - -const char *ir_expression::operator_string() -{ - return operator_string(this->operation); -} - -const char* -depth_layout_string(ir_depth_layout layout) -{ - switch(layout) { - case ir_depth_layout_none: return ""; - case ir_depth_layout_any: return "depth_any"; - case ir_depth_layout_greater: return "depth_greater"; - case ir_depth_layout_less: return "depth_less"; - case ir_depth_layout_unchanged: return "depth_unchanged"; - - default: - assert(0); - return ""; - } -} - -ir_expression_operation -ir_expression::get_operator(const char *str) -{ - const int operator_count = sizeof(operator_strs) / sizeof(operator_strs[0]); - for (int op = 0; op < operator_count; op++) { - if (strcmp(str, operator_strs[op]) == 0) - return (ir_expression_operation) op; - } - return (ir_expression_operation) -1; -} - -ir_constant::ir_constant() -{ - this->ir_type = ir_type_constant; -} - -ir_constant::ir_constant(const struct glsl_type *type, - const ir_constant_data *data) -{ - assert((type->base_type >= GLSL_TYPE_UINT) - && (type->base_type <= GLSL_TYPE_BOOL)); - - this->ir_type = ir_type_constant; - this->type = type; - memcpy(& this->value, data, sizeof(this->value)); -} - -ir_constant::ir_constant(float f) -{ - this->ir_type = ir_type_constant; - this->type = glsl_type::float_type; - this->value.f[0] = f; - for (int i = 1; i < 16; i++) { - this->value.f[i] = 0; - } -} - -ir_constant::ir_constant(unsigned int u) -{ - this->ir_type = ir_type_constant; - this->type = glsl_type::uint_type; - this->value.u[0] = u; - for (int i = 1; i < 16; i++) { - this->value.u[i] = 0; - } -} - -ir_constant::ir_constant(int i) -{ - this->ir_type = ir_type_constant; - this->type = glsl_type::int_type; - this->value.i[0] = i; - for (int i = 1; i < 16; i++) { - this->value.i[i] = 0; - } -} - -ir_constant::ir_constant(bool b) -{ - this->ir_type = ir_type_constant; - this->type = glsl_type::bool_type; - this->value.b[0] = b; - for (int i = 1; i < 16; i++) { - this->value.b[i] = false; - } -} - -ir_constant::ir_constant(const ir_constant *c, unsigned i) -{ - this->ir_type = ir_type_constant; - this->type = c->type->get_base_type(); - - switch (this->type->base_type) { - case GLSL_TYPE_UINT: this->value.u[0] = c->value.u[i]; break; - case GLSL_TYPE_INT: this->value.i[0] = c->value.i[i]; break; - case GLSL_TYPE_FLOAT: this->value.f[0] = c->value.f[i]; break; - case GLSL_TYPE_BOOL: this->value.b[0] = c->value.b[i]; break; - default: assert(!"Should not get here."); break; - } -} - -ir_constant::ir_constant(const struct glsl_type *type, exec_list *value_list) -{ - this->ir_type = ir_type_constant; - this->type = type; - - assert(type->is_scalar() || type->is_vector() || type->is_matrix() - || type->is_record() || type->is_array()); - - if (type->is_array()) { - this->array_elements = ralloc_array(this, ir_constant *, type->length); - unsigned i = 0; - foreach_list(node, value_list) { - ir_constant *value = (ir_constant *) node; - assert(value->as_constant() != NULL); - - this->array_elements[i++] = value; - } - return; - } - - /* If the constant is a record, the types of each of the entries in - * value_list must be a 1-for-1 match with the structure components. Each - * entry must also be a constant. Just move the nodes from the value_list - * to the list in the ir_constant. - */ - /* FINISHME: Should there be some type checking and / or assertions here? */ - /* FINISHME: Should the new constant take ownership of the nodes from - * FINISHME: value_list, or should it make copies? - */ - if (type->is_record()) { - value_list->move_nodes_to(& this->components); - return; - } - - for (unsigned i = 0; i < 16; i++) { - this->value.u[i] = 0; - } - - ir_constant *value = (ir_constant *) (value_list->head); - - /* Constructors with exactly one scalar argument are special for vectors - * and matrices. For vectors, the scalar value is replicated to fill all - * the components. For matrices, the scalar fills the components of the - * diagonal while the rest is filled with 0. - */ - if (value->type->is_scalar() && value->next->is_tail_sentinel()) { - if (type->is_matrix()) { - /* Matrix - fill diagonal (rest is already set to 0) */ - assert(type->base_type == GLSL_TYPE_FLOAT); - for (unsigned i = 0; i < type->matrix_columns; i++) - this->value.f[i * type->vector_elements + i] = value->value.f[0]; - } else { - /* Vector or scalar - fill all components */ - switch (type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - for (unsigned i = 0; i < type->components(); i++) - this->value.u[i] = value->value.u[0]; - break; - case GLSL_TYPE_FLOAT: - for (unsigned i = 0; i < type->components(); i++) - this->value.f[i] = value->value.f[0]; - break; - case GLSL_TYPE_BOOL: - for (unsigned i = 0; i < type->components(); i++) - this->value.b[i] = value->value.b[0]; - break; - default: - assert(!"Should not get here."); - break; - } - } - return; - } - - if (type->is_matrix() && value->type->is_matrix()) { - assert(value->next->is_tail_sentinel()); - - /* From section 5.4.2 of the GLSL 1.20 spec: - * "If a matrix is constructed from a matrix, then each component - * (column i, row j) in the result that has a corresponding component - * (column i, row j) in the argument will be initialized from there." - */ - unsigned cols = MIN2(type->matrix_columns, value->type->matrix_columns); - unsigned rows = MIN2(type->vector_elements, value->type->vector_elements); - for (unsigned i = 0; i < cols; i++) { - for (unsigned j = 0; j < rows; j++) { - const unsigned src = i * value->type->vector_elements + j; - const unsigned dst = i * type->vector_elements + j; - this->value.f[dst] = value->value.f[src]; - } - } - - /* "All other components will be initialized to the identity matrix." */ - for (unsigned i = cols; i < type->matrix_columns; i++) - this->value.f[i * type->vector_elements + i] = 1.0; - - return; - } - - /* Use each component from each entry in the value_list to initialize one - * component of the constant being constructed. - */ - for (unsigned i = 0; i < type->components(); /* empty */) { - assert(value->as_constant() != NULL); - assert(!value->is_tail_sentinel()); - - for (unsigned j = 0; j < value->type->components(); j++) { - switch (type->base_type) { - case GLSL_TYPE_UINT: - this->value.u[i] = value->get_uint_component(j); - break; - case GLSL_TYPE_INT: - this->value.i[i] = value->get_int_component(j); - break; - case GLSL_TYPE_FLOAT: - this->value.f[i] = value->get_float_component(j); - break; - case GLSL_TYPE_BOOL: - this->value.b[i] = value->get_bool_component(j); - break; - default: - /* FINISHME: What to do? Exceptions are not the answer. - */ - break; - } - - i++; - if (i >= type->components()) - break; - } - - value = (ir_constant *) value->next; - } -} - -ir_constant * -ir_constant::zero(void *mem_ctx, const glsl_type *type) -{ - assert(type->is_numeric() || type->is_boolean()); - - ir_constant *c = new(mem_ctx) ir_constant; - c->type = type; - memset(&c->value, 0, sizeof(c->value)); - - return c; -} - -bool -ir_constant::get_bool_component(unsigned i) const -{ - switch (this->type->base_type) { - case GLSL_TYPE_UINT: return this->value.u[i] != 0; - case GLSL_TYPE_INT: return this->value.i[i] != 0; - case GLSL_TYPE_FLOAT: return ((int)this->value.f[i]) != 0; - case GLSL_TYPE_BOOL: return this->value.b[i]; - default: assert(!"Should not get here."); break; - } - - /* Must return something to make the compiler happy. This is clearly an - * error case. - */ - return false; -} - -float -ir_constant::get_float_component(unsigned i) const -{ - switch (this->type->base_type) { - case GLSL_TYPE_UINT: return (float) this->value.u[i]; - case GLSL_TYPE_INT: return (float) this->value.i[i]; - case GLSL_TYPE_FLOAT: return this->value.f[i]; - case GLSL_TYPE_BOOL: return this->value.b[i] ? 1.0 : 0.0; - default: assert(!"Should not get here."); break; - } - - /* Must return something to make the compiler happy. This is clearly an - * error case. - */ - return 0.0; -} - -int -ir_constant::get_int_component(unsigned i) const -{ - switch (this->type->base_type) { - case GLSL_TYPE_UINT: return this->value.u[i]; - case GLSL_TYPE_INT: return this->value.i[i]; - case GLSL_TYPE_FLOAT: return (int) this->value.f[i]; - case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0; - default: assert(!"Should not get here."); break; - } - - /* Must return something to make the compiler happy. This is clearly an - * error case. - */ - return 0; -} - -unsigned -ir_constant::get_uint_component(unsigned i) const -{ - switch (this->type->base_type) { - case GLSL_TYPE_UINT: return this->value.u[i]; - case GLSL_TYPE_INT: return this->value.i[i]; - case GLSL_TYPE_FLOAT: return (unsigned) this->value.f[i]; - case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0; - default: assert(!"Should not get here."); break; - } - - /* Must return something to make the compiler happy. This is clearly an - * error case. - */ - return 0; -} - -ir_constant * -ir_constant::get_array_element(unsigned i) const -{ - assert(this->type->is_array()); - - /* From page 35 (page 41 of the PDF) of the GLSL 1.20 spec: - * - * "Behavior is undefined if a shader subscripts an array with an index - * less than 0 or greater than or equal to the size the array was - * declared with." - * - * Most out-of-bounds accesses are removed before things could get this far. - * There are cases where non-constant array index values can get constant - * folded. - */ - if (int(i) < 0) - i = 0; - else if (i >= this->type->length) - i = this->type->length - 1; - - return array_elements[i]; -} - -ir_constant * -ir_constant::get_record_field(const char *name) -{ - int idx = this->type->field_index(name); - - if (idx < 0) - return NULL; - - if (this->components.is_empty()) - return NULL; - - exec_node *node = this->components.head; - for (int i = 0; i < idx; i++) { - node = node->next; - - /* If the end of the list is encountered before the element matching the - * requested field is found, return NULL. - */ - if (node->is_tail_sentinel()) - return NULL; - } - - return (ir_constant *) node; -} - - -bool -ir_constant::has_value(const ir_constant *c) const -{ - if (this->type != c->type) - return false; - - if (this->type->is_array()) { - for (unsigned i = 0; i < this->type->length; i++) { - if (!this->array_elements[i]->has_value(c->array_elements[i])) - return false; - } - return true; - } - - if (this->type->base_type == GLSL_TYPE_STRUCT) { - const exec_node *a_node = this->components.head; - const exec_node *b_node = c->components.head; - - while (!a_node->is_tail_sentinel()) { - assert(!b_node->is_tail_sentinel()); - - const ir_constant *const a_field = (ir_constant *) a_node; - const ir_constant *const b_field = (ir_constant *) b_node; - - if (!a_field->has_value(b_field)) - return false; - - a_node = a_node->next; - b_node = b_node->next; - } - - return true; - } - - for (unsigned i = 0; i < this->type->components(); i++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - if (this->value.u[i] != c->value.u[i]) - return false; - break; - case GLSL_TYPE_INT: - if (this->value.i[i] != c->value.i[i]) - return false; - break; - case GLSL_TYPE_FLOAT: - if (this->value.f[i] != c->value.f[i]) - return false; - break; - case GLSL_TYPE_BOOL: - if (this->value.b[i] != c->value.b[i]) - return false; - break; - default: - assert(!"Should not get here."); - return false; - } - } - - return true; -} - -bool -ir_constant::is_zero() const -{ - if (!this->type->is_scalar() && !this->type->is_vector()) - return false; - - for (unsigned c = 0; c < this->type->vector_elements; c++) { - switch (this->type->base_type) { - case GLSL_TYPE_FLOAT: - if (this->value.f[c] != 0.0) - return false; - break; - case GLSL_TYPE_INT: - if (this->value.i[c] != 0) - return false; - break; - case GLSL_TYPE_UINT: - if (this->value.u[c] != 0) - return false; - break; - case GLSL_TYPE_BOOL: - if (this->value.b[c] != false) - return false; - break; - default: - /* The only other base types are structures, arrays, and samplers. - * Samplers cannot be constants, and the others should have been - * filtered out above. - */ - assert(!"Should not get here."); - return false; - } - } - - return true; -} - -bool -ir_constant::is_one() const -{ - if (!this->type->is_scalar() && !this->type->is_vector()) - return false; - - for (unsigned c = 0; c < this->type->vector_elements; c++) { - switch (this->type->base_type) { - case GLSL_TYPE_FLOAT: - if (this->value.f[c] != 1.0) - return false; - break; - case GLSL_TYPE_INT: - if (this->value.i[c] != 1) - return false; - break; - case GLSL_TYPE_UINT: - if (this->value.u[c] != 1) - return false; - break; - case GLSL_TYPE_BOOL: - if (this->value.b[c] != true) - return false; - break; - default: - /* The only other base types are structures, arrays, and samplers. - * Samplers cannot be constants, and the others should have been - * filtered out above. - */ - assert(!"Should not get here."); - return false; - } - } - - return true; -} - -bool -ir_constant::is_negative_one() const -{ - if (!this->type->is_scalar() && !this->type->is_vector()) - return false; - - if (this->type->is_boolean()) - return false; - - for (unsigned c = 0; c < this->type->vector_elements; c++) { - switch (this->type->base_type) { - case GLSL_TYPE_FLOAT: - if (this->value.f[c] != -1.0) - return false; - break; - case GLSL_TYPE_INT: - if (this->value.i[c] != -1) - return false; - break; - case GLSL_TYPE_UINT: - if (int(this->value.u[c]) != -1) - return false; - break; - default: - /* The only other base types are structures, arrays, samplers, and - * booleans. Samplers cannot be constants, and the others should - * have been filtered out above. - */ - assert(!"Should not get here."); - return false; - } - } - - return true; -} - -ir_loop::ir_loop() -{ - this->ir_type = ir_type_loop; - this->cmp = ir_unop_neg; - this->from = NULL; - this->to = NULL; - this->increment = NULL; - this->counter = NULL; -} - - -ir_dereference_variable::ir_dereference_variable(ir_variable *var) -{ - this->ir_type = ir_type_dereference_variable; - this->var = var; - this->type = (var != NULL) ? var->type : glsl_type::error_type; -} - - -ir_dereference_array::ir_dereference_array(ir_rvalue *value, - ir_rvalue *array_index) -{ - this->ir_type = ir_type_dereference_array; - this->array_index = array_index; - this->set_array(value); -} - - -ir_dereference_array::ir_dereference_array(ir_variable *var, - ir_rvalue *array_index) -{ - void *ctx = ralloc_parent(var); - - this->ir_type = ir_type_dereference_array; - this->array_index = array_index; - this->set_array(new(ctx) ir_dereference_variable(var)); -} - - -void -ir_dereference_array::set_array(ir_rvalue *value) -{ - this->array = value; - this->type = glsl_type::error_type; - - if (this->array != NULL) { - const glsl_type *const vt = this->array->type; - - if (vt->is_array()) { - type = vt->element_type(); - } else if (vt->is_matrix()) { - type = vt->column_type(); - } else if (vt->is_vector()) { - type = vt->get_base_type(); - } - } -} - - -ir_dereference_record::ir_dereference_record(ir_rvalue *value, - const char *field) -{ - this->ir_type = ir_type_dereference_record; - this->record = value; - this->field = ralloc_strdup(this, field); - this->type = (this->record != NULL) - ? this->record->type->field_type(field) : glsl_type::error_type; -} - - -ir_dereference_record::ir_dereference_record(ir_variable *var, - const char *field) -{ - void *ctx = ralloc_parent(var); - - this->ir_type = ir_type_dereference_record; - this->record = new(ctx) ir_dereference_variable(var); - this->field = ralloc_strdup(this, field); - this->type = (this->record != NULL) - ? this->record->type->field_type(field) : glsl_type::error_type; -} - -bool -ir_dereference::is_lvalue() const -{ - ir_variable *var = this->variable_referenced(); - - /* Every l-value derference chain eventually ends in a variable. - */ - if ((var == NULL) || var->read_only) - return false; - - if (this->type->is_array() && !var->array_lvalue) - return false; - - /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: - * - * "Samplers cannot be treated as l-values; hence cannot be used - * as out or inout function parameters, nor can they be - * assigned into." - */ - if (this->type->contains_sampler()) - return false; - - return true; -} - - -const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf", "txs" }; - -const char *ir_texture::opcode_string() -{ - assert((unsigned int) op <= - sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0])); - return tex_opcode_strs[op]; -} - -ir_texture_opcode -ir_texture::get_opcode(const char *str) -{ - const int count = sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0]); - for (int op = 0; op < count; op++) { - if (strcmp(str, tex_opcode_strs[op]) == 0) - return (ir_texture_opcode) op; - } - return (ir_texture_opcode) -1; -} - - -void -ir_texture::set_sampler(ir_dereference *sampler, const glsl_type *type) -{ - assert(sampler != NULL); - assert(type != NULL); - this->sampler = sampler; - this->type = type; - - if (this->op == ir_txs) { - assert(type->base_type == GLSL_TYPE_INT); - } else { - assert(sampler->type->sampler_type == (int) type->base_type); - if (sampler->type->sampler_shadow) - assert(type->vector_elements == 4 || type->vector_elements == 1); - else - assert(type->vector_elements == 4); - } -} - - -void -ir_swizzle::init_mask(const unsigned *comp, unsigned count) -{ - assert((count >= 1) && (count <= 4)); - - memset(&this->mask, 0, sizeof(this->mask)); - this->mask.num_components = count; - - unsigned dup_mask = 0; - switch (count) { - case 4: - assert(comp[3] <= 3); - dup_mask |= (1U << comp[3]) - & ((1U << comp[0]) | (1U << comp[1]) | (1U << comp[2])); - this->mask.w = comp[3]; - - case 3: - assert(comp[2] <= 3); - dup_mask |= (1U << comp[2]) - & ((1U << comp[0]) | (1U << comp[1])); - this->mask.z = comp[2]; - - case 2: - assert(comp[1] <= 3); - dup_mask |= (1U << comp[1]) - & ((1U << comp[0])); - this->mask.y = comp[1]; - - case 1: - assert(comp[0] <= 3); - this->mask.x = comp[0]; - } - - this->mask.has_duplicates = dup_mask != 0; - - /* Based on the number of elements in the swizzle and the base type - * (i.e., float, int, unsigned, or bool) of the vector being swizzled, - * generate the type of the resulting value. - */ - type = glsl_type::get_instance(val->type->base_type, mask.num_components, 1); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, unsigned x, unsigned y, unsigned z, - unsigned w, unsigned count) - : val(val) -{ - const unsigned components[4] = { x, y, z, w }; - this->ir_type = ir_type_swizzle; - this->init_mask(components, count); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, const unsigned *comp, - unsigned count) - : val(val) -{ - this->ir_type = ir_type_swizzle; - this->init_mask(comp, count); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask) -{ - this->ir_type = ir_type_swizzle; - this->val = val; - this->mask = mask; - this->type = glsl_type::get_instance(val->type->base_type, - mask.num_components, 1); -} - -#define X 1 -#define R 5 -#define S 9 -#define I 13 - -ir_swizzle * -ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length) -{ - void *ctx = ralloc_parent(val); - - /* For each possible swizzle character, this table encodes the value in - * \c idx_map that represents the 0th element of the vector. For invalid - * swizzle characters (e.g., 'k'), a special value is used that will allow - * detection of errors. - */ - static const unsigned char base_idx[26] = { - /* a b c d e f g h i j k l m */ - R, R, I, I, I, I, R, I, I, I, I, I, I, - /* n o p q r s t u v w x y z */ - I, I, S, S, R, S, S, I, I, X, X, X, X - }; - - /* Each valid swizzle character has an entry in the previous table. This - * table encodes the base index encoded in the previous table plus the actual - * index of the swizzle character. When processing swizzles, the first - * character in the string is indexed in the previous table. Each character - * in the string is indexed in this table, and the value found there has the - * value form the first table subtracted. The result must be on the range - * [0,3]. - * - * For example, the string "wzyx" will get X from the first table. Each of - * the charcaters will get X+3, X+2, X+1, and X+0 from this table. After - * subtraction, the swizzle values are { 3, 2, 1, 0 }. - * - * The string "wzrg" will get X from the first table. Each of the characters - * will get X+3, X+2, R+0, and R+1 from this table. After subtraction, the - * swizzle values are { 3, 2, 4, 5 }. Since 4 and 5 are outside the range - * [0,3], the error is detected. - */ - static const unsigned char idx_map[26] = { - /* a b c d e f g h i j k l m */ - R+3, R+2, 0, 0, 0, 0, R+1, 0, 0, 0, 0, 0, 0, - /* n o p q r s t u v w x y z */ - 0, 0, S+2, S+3, R+0, S+0, S+1, 0, 0, X+3, X+0, X+1, X+2 - }; - - int swiz_idx[4] = { 0, 0, 0, 0 }; - unsigned i; - - - /* Validate the first character in the swizzle string and look up the base - * index value as described above. - */ - if ((str[0] < 'a') || (str[0] > 'z')) - return NULL; - - const unsigned base = base_idx[str[0] - 'a']; - - - for (i = 0; (i < 4) && (str[i] != '\0'); i++) { - /* Validate the next character, and, as described above, convert it to a - * swizzle index. - */ - if ((str[i] < 'a') || (str[i] > 'z')) - return NULL; - - swiz_idx[i] = idx_map[str[i] - 'a'] - base; - if ((swiz_idx[i] < 0) || (swiz_idx[i] >= (int) vector_length)) - return NULL; - } - - if (str[i] != '\0') - return NULL; - - return new(ctx) ir_swizzle(val, swiz_idx[0], swiz_idx[1], swiz_idx[2], - swiz_idx[3], i); -} - -#undef X -#undef R -#undef S -#undef I - -ir_variable * -ir_swizzle::variable_referenced() const -{ - return this->val->variable_referenced(); -} - - -ir_variable::ir_variable(const struct glsl_type *type, const char *name, - ir_variable_mode mode) - : max_array_access(0), read_only(false), centroid(false), invariant(false), - mode(mode), interpolation(ir_var_smooth), array_lvalue(false) -{ - this->ir_type = ir_type_variable; - this->type = type; - this->name = ralloc_strdup(this, name); - this->explicit_location = false; - this->location = -1; - this->warn_extension = NULL; - this->constant_value = NULL; - this->origin_upper_left = false; - this->pixel_center_integer = false; - this->depth_layout = ir_depth_layout_none; - this->used = false; - - if (type && type->base_type == GLSL_TYPE_SAMPLER) - this->read_only = true; -} - - -const char * -ir_variable::interpolation_string() const -{ - switch (this->interpolation) { - case ir_var_smooth: return "smooth"; - case ir_var_flat: return "flat"; - case ir_var_noperspective: return "noperspective"; - } - - assert(!"Should not get here."); - return ""; -} - - -unsigned -ir_variable::component_slots() const -{ - /* FINISHME: Sparsely accessed arrays require fewer slots. */ - return this->type->component_slots(); -} - - -ir_function_signature::ir_function_signature(const glsl_type *return_type) - : return_type(return_type), is_defined(false), _function(NULL) -{ - this->ir_type = ir_type_function_signature; - this->is_builtin = false; -} - - -static bool -modes_match(unsigned a, unsigned b) -{ - if (a == b) - return true; - - /* Accept "in" vs. "const in" */ - if ((a == ir_var_const_in && b == ir_var_in) || - (b == ir_var_const_in && a == ir_var_in)) - return true; - - return false; -} - - -const char * -ir_function_signature::qualifiers_match(exec_list *params) -{ - exec_list_iterator iter_a = parameters.iterator(); - exec_list_iterator iter_b = params->iterator(); - - /* check that the qualifiers match. */ - while (iter_a.has_next()) { - ir_variable *a = (ir_variable *)iter_a.get(); - ir_variable *b = (ir_variable *)iter_b.get(); - - if (a->read_only != b->read_only || - !modes_match(a->mode, b->mode) || - a->interpolation != b->interpolation || - a->centroid != b->centroid) { - - /* parameter a's qualifiers don't match */ - return a->name; - } - - iter_a.next(); - iter_b.next(); - } - return NULL; -} - - -void -ir_function_signature::replace_parameters(exec_list *new_params) -{ - /* Destroy all of the previous parameter information. If the previous - * parameter information comes from the function prototype, it may either - * specify incorrect parameter names or not have names at all. - */ - foreach_iter(exec_list_iterator, iter, parameters) { - assert(((ir_instruction *) iter.get())->as_variable() != NULL); - - iter.remove(); - } - - new_params->move_nodes_to(¶meters); -} - - -ir_function::ir_function(const char *name) -{ - this->ir_type = ir_type_function; - this->name = ralloc_strdup(this, name); -} - - -bool -ir_function::has_user_signature() -{ - foreach_list(n, &this->signatures) { - ir_function_signature *const sig = (ir_function_signature *) n; - if (!sig->is_builtin) - return true; - } - return false; -} - - -ir_call * -ir_call::get_error_instruction(void *ctx) -{ - ir_call *call = new(ctx) ir_call; - - call->type = glsl_type::error_type; - return call; -} - -void -ir_call::set_callee(ir_function_signature *sig) -{ - assert((this->type == NULL) || (this->type == sig->return_type)); - - this->callee = sig; -} - -void -visit_exec_list(exec_list *list, ir_visitor *visitor) -{ - foreach_iter(exec_list_iterator, iter, *list) { - ((ir_instruction *)iter.get())->accept(visitor); - } -} - - -static void -steal_memory(ir_instruction *ir, void *new_ctx) -{ - ir_variable *var = ir->as_variable(); - ir_constant *constant = ir->as_constant(); - if (var != NULL && var->constant_value != NULL) - steal_memory(var->constant_value, ir); - - /* The components of aggregate constants are not visited by the normal - * visitor, so steal their values by hand. - */ - if (constant != NULL) { - if (constant->type->is_record()) { - foreach_iter(exec_list_iterator, iter, constant->components) { - ir_constant *field = (ir_constant *)iter.get(); - steal_memory(field, ir); - } - } else if (constant->type->is_array()) { - for (unsigned int i = 0; i < constant->type->length; i++) { - steal_memory(constant->array_elements[i], ir); - } - } - } - - ralloc_steal(new_ctx, ir); -} - - -void -reparent_ir(exec_list *list, void *mem_ctx) -{ - foreach_list(node, list) { - visit_tree((ir_instruction *) node, steal_memory, mem_ctx); - } -} - - -static ir_rvalue * -try_min_one(ir_rvalue *ir) -{ - ir_expression *expr = ir->as_expression(); - - if (!expr || expr->operation != ir_binop_min) - return NULL; - - if (expr->operands[0]->is_one()) - return expr->operands[1]; - - if (expr->operands[1]->is_one()) - return expr->operands[0]; - - return NULL; -} - -static ir_rvalue * -try_max_zero(ir_rvalue *ir) -{ - ir_expression *expr = ir->as_expression(); - - if (!expr || expr->operation != ir_binop_max) - return NULL; - - if (expr->operands[0]->is_zero()) - return expr->operands[1]; - - if (expr->operands[1]->is_zero()) - return expr->operands[0]; - - return NULL; -} - -ir_rvalue * -ir_rvalue::as_rvalue_to_saturate() -{ - ir_expression *expr = this->as_expression(); - - if (!expr) - return NULL; - - ir_rvalue *max_zero = try_max_zero(expr); - if (max_zero) { - return try_min_one(max_zero); - } else { - ir_rvalue *min_one = try_min_one(expr); - if (min_one) { - return try_max_zero(min_one); - } - } - - return NULL; -} +/* + * 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 +#include "main/core.h" /* for MAX2 */ +#include "ir.h" +#include "ir_visitor.h" +#include "glsl_types.h" + +ir_rvalue::ir_rvalue() +{ + this->type = glsl_type::error_type; +} + +bool ir_rvalue::is_zero() const +{ + return false; +} + +bool ir_rvalue::is_one() const +{ + return false; +} + +bool ir_rvalue::is_negative_one() const +{ + return false; +} + +/** + * Modify the swizzle make to move one component to another + * + * \param m IR swizzle to be modified + * \param from Component in the RHS that is to be swizzled + * \param to Desired swizzle location of \c from + */ +static void +update_rhs_swizzle(ir_swizzle_mask &m, unsigned from, unsigned to) +{ + switch (to) { + case 0: m.x = from; break; + case 1: m.y = from; break; + case 2: m.z = from; break; + case 3: m.w = from; break; + default: assert(!"Should not get here."); + } + + m.num_components = MAX2(m.num_components, (to + 1)); +} + +void +ir_assignment::set_lhs(ir_rvalue *lhs) +{ + void *mem_ctx = this; + bool swizzled = false; + + while (lhs != NULL) { + ir_swizzle *swiz = lhs->as_swizzle(); + + if (swiz == NULL) + break; + + unsigned write_mask = 0; + ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 }; + + for (unsigned i = 0; i < swiz->mask.num_components; i++) { + unsigned c = 0; + + switch (i) { + case 0: c = swiz->mask.x; break; + case 1: c = swiz->mask.y; break; + case 2: c = swiz->mask.z; break; + case 3: c = swiz->mask.w; break; + default: assert(!"Should not get here."); + } + + write_mask |= (((this->write_mask >> i) & 1) << c); + update_rhs_swizzle(rhs_swiz, i, c); + } + + this->write_mask = write_mask; + lhs = swiz->val; + + this->rhs = new(mem_ctx) ir_swizzle(this->rhs, rhs_swiz); + swizzled = true; + } + + if (swizzled) { + /* Now, RHS channels line up with the LHS writemask. Collapse it + * to just the channels that will be written. + */ + ir_swizzle_mask rhs_swiz = { 0, 0, 0, 0, 0, 0 }; + int rhs_chan = 0; + for (int i = 0; i < 4; i++) { + if (write_mask & (1 << i)) + update_rhs_swizzle(rhs_swiz, i, rhs_chan++); + } + this->rhs = new(mem_ctx) ir_swizzle(this->rhs, rhs_swiz); + } + + assert((lhs == NULL) || lhs->as_dereference()); + + this->lhs = (ir_dereference *) lhs; +} + +ir_variable * +ir_assignment::whole_variable_written() +{ + ir_variable *v = this->lhs->whole_variable_referenced(); + + if (v == NULL) + return NULL; + + if (v->type->is_scalar()) + return v; + + if (v->type->is_vector()) { + const unsigned mask = (1U << v->type->vector_elements) - 1; + + if (mask != this->write_mask) + return NULL; + } + + /* Either all the vector components are assigned or the variable is some + * composite type (and the whole thing is assigned. + */ + return v; +} + +ir_assignment::ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, + ir_rvalue *condition, unsigned write_mask) +{ + this->ir_type = ir_type_assignment; + this->condition = condition; + this->rhs = rhs; + this->lhs = lhs; + this->write_mask = write_mask; + + if (lhs->type->is_scalar() || lhs->type->is_vector()) { + int lhs_components = 0; + for (int i = 0; i < 4; i++) { + if (write_mask & (1 << i)) + lhs_components++; + } + + assert(lhs_components == this->rhs->type->vector_elements); + } +} + +ir_assignment::ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, + ir_rvalue *condition) +{ + this->ir_type = ir_type_assignment; + this->condition = condition; + this->rhs = rhs; + + /* If the RHS is a vector type, assume that all components of the vector + * type are being written to the LHS. The write mask comes from the RHS + * because we can have a case where the LHS is a vec4 and the RHS is a + * vec3. In that case, the assignment is: + * + * (assign (...) (xyz) (var_ref lhs) (var_ref rhs)) + */ + if (rhs->type->is_vector()) + this->write_mask = (1U << rhs->type->vector_elements) - 1; + else if (rhs->type->is_scalar()) + this->write_mask = 1; + else + this->write_mask = 0; + + this->set_lhs(lhs); +} + + +ir_expression::ir_expression(int op, const struct glsl_type *type, + ir_rvalue *op0) +{ + assert(get_num_operands(ir_expression_operation(op)) == 1); + this->ir_type = ir_type_expression; + this->type = type; + this->operation = ir_expression_operation(op); + this->operands[0] = op0; + this->operands[1] = NULL; + this->operands[2] = NULL; + this->operands[3] = NULL; +} + +ir_expression::ir_expression(int op, const struct glsl_type *type, + ir_rvalue *op0, ir_rvalue *op1) +{ + assert(((op1 == NULL) && (get_num_operands(ir_expression_operation(op)) == 1)) + || (get_num_operands(ir_expression_operation(op)) == 2)); + this->ir_type = ir_type_expression; + this->type = type; + this->operation = ir_expression_operation(op); + this->operands[0] = op0; + this->operands[1] = op1; + this->operands[2] = NULL; + this->operands[3] = NULL; +} + +ir_expression::ir_expression(int op, const struct glsl_type *type, + ir_rvalue *op0, ir_rvalue *op1, + ir_rvalue *op2, ir_rvalue *op3) +{ + this->ir_type = ir_type_expression; + this->type = type; + this->operation = ir_expression_operation(op); + this->operands[0] = op0; + this->operands[1] = op1; + this->operands[2] = op2; + this->operands[3] = op3; +} + +ir_expression::ir_expression(int op, ir_rvalue *op0) +{ + this->ir_type = ir_type_expression; + + this->operation = ir_expression_operation(op); + this->operands[0] = op0; + this->operands[1] = NULL; + this->operands[2] = NULL; + this->operands[3] = NULL; + + assert(op <= ir_last_unop); + + switch (this->operation) { + case ir_unop_bit_not: + case ir_unop_logic_not: + case ir_unop_neg: + case ir_unop_abs: + case ir_unop_sign: + case ir_unop_rcp: + case ir_unop_rsq: + case ir_unop_sqrt: + case ir_unop_exp: + case ir_unop_log: + case ir_unop_exp2: + case ir_unop_log2: + case ir_unop_trunc: + case ir_unop_ceil: + case ir_unop_floor: + case ir_unop_fract: + case ir_unop_round_even: + case ir_unop_sin: + case ir_unop_cos: + case ir_unop_sin_reduced: + case ir_unop_cos_reduced: + case ir_unop_dFdx: + case ir_unop_dFdy: + this->type = op0->type; + break; + + case ir_unop_f2i: + case ir_unop_b2i: + case ir_unop_u2i: + this->type = glsl_type::get_instance(GLSL_TYPE_INT, + op0->type->vector_elements, 1); + break; + + case ir_unop_b2f: + case ir_unop_i2f: + case ir_unop_u2f: + this->type = glsl_type::get_instance(GLSL_TYPE_FLOAT, + op0->type->vector_elements, 1); + break; + + case ir_unop_f2b: + case ir_unop_i2b: + this->type = glsl_type::get_instance(GLSL_TYPE_BOOL, + op0->type->vector_elements, 1); + break; + + case ir_unop_i2u: + this->type = glsl_type::get_instance(GLSL_TYPE_UINT, + op0->type->vector_elements, 1); + break; + + case ir_unop_noise: + this->type = glsl_type::float_type; + break; + + case ir_unop_any: + this->type = glsl_type::bool_type; + break; + + default: + assert(!"not reached: missing automatic type setup for ir_expression"); + this->type = op0->type; + break; + } +} + +ir_expression::ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1) +{ + this->ir_type = ir_type_expression; + + this->operation = ir_expression_operation(op); + this->operands[0] = op0; + this->operands[1] = op1; + this->operands[2] = NULL; + this->operands[3] = NULL; + + assert(op > ir_last_unop); + + switch (this->operation) { + case ir_binop_all_equal: + case ir_binop_any_nequal: + this->type = glsl_type::bool_type; + break; + + case ir_binop_add: + case ir_binop_sub: + case ir_binop_min: + case ir_binop_max: + case ir_binop_pow: + case ir_binop_mul: + case ir_binop_div: + case ir_binop_mod: + if (op0->type->is_scalar()) { + this->type = op1->type; + } else if (op1->type->is_scalar()) { + this->type = op0->type; + } else { + /* FINISHME: matrix types */ + assert(!op0->type->is_matrix() && !op1->type->is_matrix()); + assert(op0->type == op1->type); + this->type = op0->type; + } + break; + + case ir_binop_logic_and: + case ir_binop_logic_xor: + case ir_binop_logic_or: + case ir_binop_bit_and: + case ir_binop_bit_xor: + case ir_binop_bit_or: + if (op0->type->is_scalar()) { + this->type = op1->type; + } else if (op1->type->is_scalar()) { + this->type = op0->type; + } + break; + + case ir_binop_equal: + case ir_binop_nequal: + case ir_binop_lequal: + case ir_binop_gequal: + case ir_binop_less: + case ir_binop_greater: + assert(op0->type == op1->type); + this->type = glsl_type::get_instance(GLSL_TYPE_BOOL, + op0->type->vector_elements, 1); + break; + + case ir_binop_dot: + this->type = glsl_type::float_type; + break; + + case ir_binop_lshift: + case ir_binop_rshift: + this->type = op0->type; + break; + + default: + assert(!"not reached: missing automatic type setup for ir_expression"); + this->type = glsl_type::float_type; + } +} + +unsigned int +ir_expression::get_num_operands(ir_expression_operation op) +{ + assert(op <= ir_last_opcode); + + if (op <= ir_last_unop) + return 1; + + if (op <= ir_last_binop) + return 2; + + if (op == ir_quadop_vector) + return 4; + + assert(false); + return 0; +} + +static const char *const operator_strs[] = { + "~", + "!", + "neg", + "abs", + "sign", + "rcp", + "rsq", + "sqrt", + "exp", + "log", + "exp2", + "log2", + "f2i", + "i2f", + "f2b", + "b2f", + "i2b", + "b2i", + "u2f", + "i2u", + "u2i", + "any", + "trunc", + "ceil", + "floor", + "fract", + "round_even", + "sin", + "cos", + "sin_reduced", + "cos_reduced", + "dFdx", + "dFdy", + "noise", + "+", + "-", + "*", + "/", + "%", + "<", + ">", + "<=", + ">=", + "==", + "!=", + "all_equal", + "any_nequal", + "<<", + ">>", + "&", + "^", + "|", + "&&", + "^^", + "||", + "dot", + "min", + "max", + "pow", + "vector", +}; + +const char *ir_expression::operator_string(ir_expression_operation op) +{ + assert((unsigned int) op < Elements(operator_strs)); + assert(Elements(operator_strs) == (ir_quadop_vector + 1)); + return operator_strs[op]; +} + +const char *ir_expression::operator_string() +{ + return operator_string(this->operation); +} + +const char* +depth_layout_string(ir_depth_layout layout) +{ + switch(layout) { + case ir_depth_layout_none: return ""; + case ir_depth_layout_any: return "depth_any"; + case ir_depth_layout_greater: return "depth_greater"; + case ir_depth_layout_less: return "depth_less"; + case ir_depth_layout_unchanged: return "depth_unchanged"; + + default: + assert(0); + return ""; + } +} + +ir_expression_operation +ir_expression::get_operator(const char *str) +{ + const int operator_count = sizeof(operator_strs) / sizeof(operator_strs[0]); + for (int op = 0; op < operator_count; op++) { + if (strcmp(str, operator_strs[op]) == 0) + return (ir_expression_operation) op; + } + return (ir_expression_operation) -1; +} + +ir_constant::ir_constant() +{ + this->ir_type = ir_type_constant; +} + +ir_constant::ir_constant(const struct glsl_type *type, + const ir_constant_data *data) +{ + assert((type->base_type >= GLSL_TYPE_UINT) + && (type->base_type <= GLSL_TYPE_BOOL)); + + this->ir_type = ir_type_constant; + this->type = type; + memcpy(& this->value, data, sizeof(this->value)); +} + +ir_constant::ir_constant(float f) +{ + this->ir_type = ir_type_constant; + this->type = glsl_type::float_type; + this->value.f[0] = f; + for (int i = 1; i < 16; i++) { + this->value.f[i] = 0; + } +} + +ir_constant::ir_constant(unsigned int u) +{ + this->ir_type = ir_type_constant; + this->type = glsl_type::uint_type; + this->value.u[0] = u; + for (int i = 1; i < 16; i++) { + this->value.u[i] = 0; + } +} + +ir_constant::ir_constant(int i) +{ + this->ir_type = ir_type_constant; + this->type = glsl_type::int_type; + this->value.i[0] = i; + for (int i = 1; i < 16; i++) { + this->value.i[i] = 0; + } +} + +ir_constant::ir_constant(bool b) +{ + this->ir_type = ir_type_constant; + this->type = glsl_type::bool_type; + this->value.b[0] = b; + for (int i = 1; i < 16; i++) { + this->value.b[i] = false; + } +} + +ir_constant::ir_constant(const ir_constant *c, unsigned i) +{ + this->ir_type = ir_type_constant; + this->type = c->type->get_base_type(); + + switch (this->type->base_type) { + case GLSL_TYPE_UINT: this->value.u[0] = c->value.u[i]; break; + case GLSL_TYPE_INT: this->value.i[0] = c->value.i[i]; break; + case GLSL_TYPE_FLOAT: this->value.f[0] = c->value.f[i]; break; + case GLSL_TYPE_BOOL: this->value.b[0] = c->value.b[i]; break; + default: assert(!"Should not get here."); break; + } +} + +ir_constant::ir_constant(const struct glsl_type *type, exec_list *value_list) +{ + this->ir_type = ir_type_constant; + this->type = type; + + assert(type->is_scalar() || type->is_vector() || type->is_matrix() + || type->is_record() || type->is_array()); + + if (type->is_array()) { + this->array_elements = ralloc_array(this, ir_constant *, type->length); + unsigned i = 0; + foreach_list(node, value_list) { + ir_constant *value = (ir_constant *) node; + assert(value->as_constant() != NULL); + + this->array_elements[i++] = value; + } + return; + } + + /* If the constant is a record, the types of each of the entries in + * value_list must be a 1-for-1 match with the structure components. Each + * entry must also be a constant. Just move the nodes from the value_list + * to the list in the ir_constant. + */ + /* FINISHME: Should there be some type checking and / or assertions here? */ + /* FINISHME: Should the new constant take ownership of the nodes from + * FINISHME: value_list, or should it make copies? + */ + if (type->is_record()) { + value_list->move_nodes_to(& this->components); + return; + } + + for (unsigned i = 0; i < 16; i++) { + this->value.u[i] = 0; + } + + ir_constant *value = (ir_constant *) (value_list->head); + + /* Constructors with exactly one scalar argument are special for vectors + * and matrices. For vectors, the scalar value is replicated to fill all + * the components. For matrices, the scalar fills the components of the + * diagonal while the rest is filled with 0. + */ + if (value->type->is_scalar() && value->next->is_tail_sentinel()) { + if (type->is_matrix()) { + /* Matrix - fill diagonal (rest is already set to 0) */ + assert(type->base_type == GLSL_TYPE_FLOAT); + for (unsigned i = 0; i < type->matrix_columns; i++) + this->value.f[i * type->vector_elements + i] = value->value.f[0]; + } else { + /* Vector or scalar - fill all components */ + switch (type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + for (unsigned i = 0; i < type->components(); i++) + this->value.u[i] = value->value.u[0]; + break; + case GLSL_TYPE_FLOAT: + for (unsigned i = 0; i < type->components(); i++) + this->value.f[i] = value->value.f[0]; + break; + case GLSL_TYPE_BOOL: + for (unsigned i = 0; i < type->components(); i++) + this->value.b[i] = value->value.b[0]; + break; + default: + assert(!"Should not get here."); + break; + } + } + return; + } + + if (type->is_matrix() && value->type->is_matrix()) { + assert(value->next->is_tail_sentinel()); + + /* From section 5.4.2 of the GLSL 1.20 spec: + * "If a matrix is constructed from a matrix, then each component + * (column i, row j) in the result that has a corresponding component + * (column i, row j) in the argument will be initialized from there." + */ + unsigned cols = MIN2(type->matrix_columns, value->type->matrix_columns); + unsigned rows = MIN2(type->vector_elements, value->type->vector_elements); + for (unsigned i = 0; i < cols; i++) { + for (unsigned j = 0; j < rows; j++) { + const unsigned src = i * value->type->vector_elements + j; + const unsigned dst = i * type->vector_elements + j; + this->value.f[dst] = value->value.f[src]; + } + } + + /* "All other components will be initialized to the identity matrix." */ + for (unsigned i = cols; i < type->matrix_columns; i++) + this->value.f[i * type->vector_elements + i] = 1.0; + + return; + } + + /* Use each component from each entry in the value_list to initialize one + * component of the constant being constructed. + */ + for (unsigned i = 0; i < type->components(); /* empty */) { + assert(value->as_constant() != NULL); + assert(!value->is_tail_sentinel()); + + for (unsigned j = 0; j < value->type->components(); j++) { + switch (type->base_type) { + case GLSL_TYPE_UINT: + this->value.u[i] = value->get_uint_component(j); + break; + case GLSL_TYPE_INT: + this->value.i[i] = value->get_int_component(j); + break; + case GLSL_TYPE_FLOAT: + this->value.f[i] = value->get_float_component(j); + break; + case GLSL_TYPE_BOOL: + this->value.b[i] = value->get_bool_component(j); + break; + default: + /* FINISHME: What to do? Exceptions are not the answer. + */ + break; + } + + i++; + if (i >= type->components()) + break; + } + + value = (ir_constant *) value->next; + } +} + +ir_constant * +ir_constant::zero(void *mem_ctx, const glsl_type *type) +{ + assert(type->is_numeric() || type->is_boolean()); + + ir_constant *c = new(mem_ctx) ir_constant; + c->type = type; + memset(&c->value, 0, sizeof(c->value)); + + return c; +} + +bool +ir_constant::get_bool_component(unsigned i) const +{ + switch (this->type->base_type) { + case GLSL_TYPE_UINT: return this->value.u[i] != 0; + case GLSL_TYPE_INT: return this->value.i[i] != 0; + case GLSL_TYPE_FLOAT: return ((int)this->value.f[i]) != 0; + case GLSL_TYPE_BOOL: return this->value.b[i]; + default: assert(!"Should not get here."); break; + } + + /* Must return something to make the compiler happy. This is clearly an + * error case. + */ + return false; +} + +float +ir_constant::get_float_component(unsigned i) const +{ + switch (this->type->base_type) { + case GLSL_TYPE_UINT: return (float) this->value.u[i]; + case GLSL_TYPE_INT: return (float) this->value.i[i]; + case GLSL_TYPE_FLOAT: return this->value.f[i]; + case GLSL_TYPE_BOOL: return this->value.b[i] ? 1.0 : 0.0; + default: assert(!"Should not get here."); break; + } + + /* Must return something to make the compiler happy. This is clearly an + * error case. + */ + return 0.0; +} + +int +ir_constant::get_int_component(unsigned i) const +{ + switch (this->type->base_type) { + case GLSL_TYPE_UINT: return this->value.u[i]; + case GLSL_TYPE_INT: return this->value.i[i]; + case GLSL_TYPE_FLOAT: return (int) this->value.f[i]; + case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0; + default: assert(!"Should not get here."); break; + } + + /* Must return something to make the compiler happy. This is clearly an + * error case. + */ + return 0; +} + +unsigned +ir_constant::get_uint_component(unsigned i) const +{ + switch (this->type->base_type) { + case GLSL_TYPE_UINT: return this->value.u[i]; + case GLSL_TYPE_INT: return this->value.i[i]; + case GLSL_TYPE_FLOAT: return (unsigned) this->value.f[i]; + case GLSL_TYPE_BOOL: return this->value.b[i] ? 1 : 0; + default: assert(!"Should not get here."); break; + } + + /* Must return something to make the compiler happy. This is clearly an + * error case. + */ + return 0; +} + +ir_constant * +ir_constant::get_array_element(unsigned i) const +{ + assert(this->type->is_array()); + + /* From page 35 (page 41 of the PDF) of the GLSL 1.20 spec: + * + * "Behavior is undefined if a shader subscripts an array with an index + * less than 0 or greater than or equal to the size the array was + * declared with." + * + * Most out-of-bounds accesses are removed before things could get this far. + * There are cases where non-constant array index values can get constant + * folded. + */ + if (int(i) < 0) + i = 0; + else if (i >= this->type->length) + i = this->type->length - 1; + + return array_elements[i]; +} + +ir_constant * +ir_constant::get_record_field(const char *name) +{ + int idx = this->type->field_index(name); + + if (idx < 0) + return NULL; + + if (this->components.is_empty()) + return NULL; + + exec_node *node = this->components.head; + for (int i = 0; i < idx; i++) { + node = node->next; + + /* If the end of the list is encountered before the element matching the + * requested field is found, return NULL. + */ + if (node->is_tail_sentinel()) + return NULL; + } + + return (ir_constant *) node; +} + + +bool +ir_constant::has_value(const ir_constant *c) const +{ + if (this->type != c->type) + return false; + + if (this->type->is_array()) { + for (unsigned i = 0; i < this->type->length; i++) { + if (!this->array_elements[i]->has_value(c->array_elements[i])) + return false; + } + return true; + } + + if (this->type->base_type == GLSL_TYPE_STRUCT) { + const exec_node *a_node = this->components.head; + const exec_node *b_node = c->components.head; + + while (!a_node->is_tail_sentinel()) { + assert(!b_node->is_tail_sentinel()); + + const ir_constant *const a_field = (ir_constant *) a_node; + const ir_constant *const b_field = (ir_constant *) b_node; + + if (!a_field->has_value(b_field)) + return false; + + a_node = a_node->next; + b_node = b_node->next; + } + + return true; + } + + for (unsigned i = 0; i < this->type->components(); i++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + if (this->value.u[i] != c->value.u[i]) + return false; + break; + case GLSL_TYPE_INT: + if (this->value.i[i] != c->value.i[i]) + return false; + break; + case GLSL_TYPE_FLOAT: + if (this->value.f[i] != c->value.f[i]) + return false; + break; + case GLSL_TYPE_BOOL: + if (this->value.b[i] != c->value.b[i]) + return false; + break; + default: + assert(!"Should not get here."); + return false; + } + } + + return true; +} + +bool +ir_constant::is_zero() const +{ + if (!this->type->is_scalar() && !this->type->is_vector()) + return false; + + for (unsigned c = 0; c < this->type->vector_elements; c++) { + switch (this->type->base_type) { + case GLSL_TYPE_FLOAT: + if (this->value.f[c] != 0.0) + return false; + break; + case GLSL_TYPE_INT: + if (this->value.i[c] != 0) + return false; + break; + case GLSL_TYPE_UINT: + if (this->value.u[c] != 0) + return false; + break; + case GLSL_TYPE_BOOL: + if (this->value.b[c] != false) + return false; + break; + default: + /* The only other base types are structures, arrays, and samplers. + * Samplers cannot be constants, and the others should have been + * filtered out above. + */ + assert(!"Should not get here."); + return false; + } + } + + return true; +} + +bool +ir_constant::is_one() const +{ + if (!this->type->is_scalar() && !this->type->is_vector()) + return false; + + for (unsigned c = 0; c < this->type->vector_elements; c++) { + switch (this->type->base_type) { + case GLSL_TYPE_FLOAT: + if (this->value.f[c] != 1.0) + return false; + break; + case GLSL_TYPE_INT: + if (this->value.i[c] != 1) + return false; + break; + case GLSL_TYPE_UINT: + if (this->value.u[c] != 1) + return false; + break; + case GLSL_TYPE_BOOL: + if (this->value.b[c] != true) + return false; + break; + default: + /* The only other base types are structures, arrays, and samplers. + * Samplers cannot be constants, and the others should have been + * filtered out above. + */ + assert(!"Should not get here."); + return false; + } + } + + return true; +} + +bool +ir_constant::is_negative_one() const +{ + if (!this->type->is_scalar() && !this->type->is_vector()) + return false; + + if (this->type->is_boolean()) + return false; + + for (unsigned c = 0; c < this->type->vector_elements; c++) { + switch (this->type->base_type) { + case GLSL_TYPE_FLOAT: + if (this->value.f[c] != -1.0) + return false; + break; + case GLSL_TYPE_INT: + if (this->value.i[c] != -1) + return false; + break; + case GLSL_TYPE_UINT: + if (int(this->value.u[c]) != -1) + return false; + break; + default: + /* The only other base types are structures, arrays, samplers, and + * booleans. Samplers cannot be constants, and the others should + * have been filtered out above. + */ + assert(!"Should not get here."); + return false; + } + } + + return true; +} + +ir_loop::ir_loop() +{ + this->ir_type = ir_type_loop; + this->cmp = ir_unop_neg; + this->from = NULL; + this->to = NULL; + this->increment = NULL; + this->counter = NULL; +} + + +ir_dereference_variable::ir_dereference_variable(ir_variable *var) +{ + this->ir_type = ir_type_dereference_variable; + this->var = var; + this->type = (var != NULL) ? var->type : glsl_type::error_type; +} + + +ir_dereference_array::ir_dereference_array(ir_rvalue *value, + ir_rvalue *array_index) +{ + this->ir_type = ir_type_dereference_array; + this->array_index = array_index; + this->set_array(value); +} + + +ir_dereference_array::ir_dereference_array(ir_variable *var, + ir_rvalue *array_index) +{ + void *ctx = ralloc_parent(var); + + this->ir_type = ir_type_dereference_array; + this->array_index = array_index; + this->set_array(new(ctx) ir_dereference_variable(var)); +} + + +void +ir_dereference_array::set_array(ir_rvalue *value) +{ + this->array = value; + this->type = glsl_type::error_type; + + if (this->array != NULL) { + const glsl_type *const vt = this->array->type; + + if (vt->is_array()) { + type = vt->element_type(); + } else if (vt->is_matrix()) { + type = vt->column_type(); + } else if (vt->is_vector()) { + type = vt->get_base_type(); + } + } +} + + +ir_dereference_record::ir_dereference_record(ir_rvalue *value, + const char *field) +{ + this->ir_type = ir_type_dereference_record; + this->record = value; + this->field = ralloc_strdup(this, field); + this->type = (this->record != NULL) + ? this->record->type->field_type(field) : glsl_type::error_type; +} + + +ir_dereference_record::ir_dereference_record(ir_variable *var, + const char *field) +{ + void *ctx = ralloc_parent(var); + + this->ir_type = ir_type_dereference_record; + this->record = new(ctx) ir_dereference_variable(var); + this->field = ralloc_strdup(this, field); + this->type = (this->record != NULL) + ? this->record->type->field_type(field) : glsl_type::error_type; +} + +bool +ir_dereference::is_lvalue() const +{ + ir_variable *var = this->variable_referenced(); + + /* Every l-value derference chain eventually ends in a variable. + */ + if ((var == NULL) || var->read_only) + return false; + + if (this->type->is_array() && !var->array_lvalue) + return false; + + /* From page 17 (page 23 of the PDF) of the GLSL 1.20 spec: + * + * "Samplers cannot be treated as l-values; hence cannot be used + * as out or inout function parameters, nor can they be + * assigned into." + */ + if (this->type->contains_sampler()) + return false; + + return true; +} + + +const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf", "txs" }; + +const char *ir_texture::opcode_string() +{ + assert((unsigned int) op <= + sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0])); + return tex_opcode_strs[op]; +} + +ir_texture_opcode +ir_texture::get_opcode(const char *str) +{ + const int count = sizeof(tex_opcode_strs) / sizeof(tex_opcode_strs[0]); + for (int op = 0; op < count; op++) { + if (strcmp(str, tex_opcode_strs[op]) == 0) + return (ir_texture_opcode) op; + } + return (ir_texture_opcode) -1; +} + + +void +ir_texture::set_sampler(ir_dereference *sampler, const glsl_type *type) +{ + assert(sampler != NULL); + assert(type != NULL); + this->sampler = sampler; + this->type = type; + + if (this->op == ir_txs) { + assert(type->base_type == GLSL_TYPE_INT); + } else { + assert(sampler->type->sampler_type == (int) type->base_type); + if (sampler->type->sampler_shadow) + assert(type->vector_elements == 4 || type->vector_elements == 1); + else + assert(type->vector_elements == 4); + } +} + + +void +ir_swizzle::init_mask(const unsigned *comp, unsigned count) +{ + assert((count >= 1) && (count <= 4)); + + memset(&this->mask, 0, sizeof(this->mask)); + this->mask.num_components = count; + + unsigned dup_mask = 0; + switch (count) { + case 4: + assert(comp[3] <= 3); + dup_mask |= (1U << comp[3]) + & ((1U << comp[0]) | (1U << comp[1]) | (1U << comp[2])); + this->mask.w = comp[3]; + + case 3: + assert(comp[2] <= 3); + dup_mask |= (1U << comp[2]) + & ((1U << comp[0]) | (1U << comp[1])); + this->mask.z = comp[2]; + + case 2: + assert(comp[1] <= 3); + dup_mask |= (1U << comp[1]) + & ((1U << comp[0])); + this->mask.y = comp[1]; + + case 1: + assert(comp[0] <= 3); + this->mask.x = comp[0]; + } + + this->mask.has_duplicates = dup_mask != 0; + + /* Based on the number of elements in the swizzle and the base type + * (i.e., float, int, unsigned, or bool) of the vector being swizzled, + * generate the type of the resulting value. + */ + type = glsl_type::get_instance(val->type->base_type, mask.num_components, 1); +} + +ir_swizzle::ir_swizzle(ir_rvalue *val, unsigned x, unsigned y, unsigned z, + unsigned w, unsigned count) + : val(val) +{ + const unsigned components[4] = { x, y, z, w }; + this->ir_type = ir_type_swizzle; + this->init_mask(components, count); +} + +ir_swizzle::ir_swizzle(ir_rvalue *val, const unsigned *comp, + unsigned count) + : val(val) +{ + this->ir_type = ir_type_swizzle; + this->init_mask(comp, count); +} + +ir_swizzle::ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask) +{ + this->ir_type = ir_type_swizzle; + this->val = val; + this->mask = mask; + this->type = glsl_type::get_instance(val->type->base_type, + mask.num_components, 1); +} + +#define X 1 +#define R 5 +#define S 9 +#define I 13 + +ir_swizzle * +ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length) +{ + void *ctx = ralloc_parent(val); + + /* For each possible swizzle character, this table encodes the value in + * \c idx_map that represents the 0th element of the vector. For invalid + * swizzle characters (e.g., 'k'), a special value is used that will allow + * detection of errors. + */ + static const unsigned char base_idx[26] = { + /* a b c d e f g h i j k l m */ + R, R, I, I, I, I, R, I, I, I, I, I, I, + /* n o p q r s t u v w x y z */ + I, I, S, S, R, S, S, I, I, X, X, X, X + }; + + /* Each valid swizzle character has an entry in the previous table. This + * table encodes the base index encoded in the previous table plus the actual + * index of the swizzle character. When processing swizzles, the first + * character in the string is indexed in the previous table. Each character + * in the string is indexed in this table, and the value found there has the + * value form the first table subtracted. The result must be on the range + * [0,3]. + * + * For example, the string "wzyx" will get X from the first table. Each of + * the charcaters will get X+3, X+2, X+1, and X+0 from this table. After + * subtraction, the swizzle values are { 3, 2, 1, 0 }. + * + * The string "wzrg" will get X from the first table. Each of the characters + * will get X+3, X+2, R+0, and R+1 from this table. After subtraction, the + * swizzle values are { 3, 2, 4, 5 }. Since 4 and 5 are outside the range + * [0,3], the error is detected. + */ + static const unsigned char idx_map[26] = { + /* a b c d e f g h i j k l m */ + R+3, R+2, 0, 0, 0, 0, R+1, 0, 0, 0, 0, 0, 0, + /* n o p q r s t u v w x y z */ + 0, 0, S+2, S+3, R+0, S+0, S+1, 0, 0, X+3, X+0, X+1, X+2 + }; + + int swiz_idx[4] = { 0, 0, 0, 0 }; + unsigned i; + + + /* Validate the first character in the swizzle string and look up the base + * index value as described above. + */ + if ((str[0] < 'a') || (str[0] > 'z')) + return NULL; + + const unsigned base = base_idx[str[0] - 'a']; + + + for (i = 0; (i < 4) && (str[i] != '\0'); i++) { + /* Validate the next character, and, as described above, convert it to a + * swizzle index. + */ + if ((str[i] < 'a') || (str[i] > 'z')) + return NULL; + + swiz_idx[i] = idx_map[str[i] - 'a'] - base; + if ((swiz_idx[i] < 0) || (swiz_idx[i] >= (int) vector_length)) + return NULL; + } + + if (str[i] != '\0') + return NULL; + + return new(ctx) ir_swizzle(val, swiz_idx[0], swiz_idx[1], swiz_idx[2], + swiz_idx[3], i); +} + +#undef X +#undef R +#undef S +#undef I + +ir_variable * +ir_swizzle::variable_referenced() const +{ + return this->val->variable_referenced(); +} + + +ir_variable::ir_variable(const struct glsl_type *type, const char *name, + ir_variable_mode mode) + : max_array_access(0), read_only(false), centroid(false), invariant(false), + mode(mode), interpolation(ir_var_smooth), array_lvalue(false) +{ + this->ir_type = ir_type_variable; + this->type = type; + this->name = ralloc_strdup(this, name); + this->explicit_location = false; + this->location = -1; + this->warn_extension = NULL; + this->constant_value = NULL; + this->origin_upper_left = false; + this->pixel_center_integer = false; + this->depth_layout = ir_depth_layout_none; + this->used = false; + + if (type && type->base_type == GLSL_TYPE_SAMPLER) + this->read_only = true; +} + + +const char * +ir_variable::interpolation_string() const +{ + switch (this->interpolation) { + case ir_var_smooth: return "smooth"; + case ir_var_flat: return "flat"; + case ir_var_noperspective: return "noperspective"; + } + + assert(!"Should not get here."); + return ""; +} + + +unsigned +ir_variable::component_slots() const +{ + /* FINISHME: Sparsely accessed arrays require fewer slots. */ + return this->type->component_slots(); +} + + +ir_function_signature::ir_function_signature(const glsl_type *return_type) + : return_type(return_type), is_defined(false), _function(NULL) +{ + this->ir_type = ir_type_function_signature; + this->is_builtin = false; +} + + +static bool +modes_match(unsigned a, unsigned b) +{ + if (a == b) + return true; + + /* Accept "in" vs. "const in" */ + if ((a == ir_var_const_in && b == ir_var_in) || + (b == ir_var_const_in && a == ir_var_in)) + return true; + + return false; +} + + +const char * +ir_function_signature::qualifiers_match(exec_list *params) +{ + exec_list_iterator iter_a = parameters.iterator(); + exec_list_iterator iter_b = params->iterator(); + + /* check that the qualifiers match. */ + while (iter_a.has_next()) { + ir_variable *a = (ir_variable *)iter_a.get(); + ir_variable *b = (ir_variable *)iter_b.get(); + + if (a->read_only != b->read_only || + !modes_match(a->mode, b->mode) || + a->interpolation != b->interpolation || + a->centroid != b->centroid) { + + /* parameter a's qualifiers don't match */ + return a->name; + } + + iter_a.next(); + iter_b.next(); + } + return NULL; +} + + +void +ir_function_signature::replace_parameters(exec_list *new_params) +{ + /* Destroy all of the previous parameter information. If the previous + * parameter information comes from the function prototype, it may either + * specify incorrect parameter names or not have names at all. + */ + foreach_iter(exec_list_iterator, iter, parameters) { + assert(((ir_instruction *) iter.get())->as_variable() != NULL); + + iter.remove(); + } + + new_params->move_nodes_to(¶meters); +} + + +ir_function::ir_function(const char *name) +{ + this->ir_type = ir_type_function; + this->name = ralloc_strdup(this, name); +} + + +bool +ir_function::has_user_signature() +{ + foreach_list(n, &this->signatures) { + ir_function_signature *const sig = (ir_function_signature *) n; + if (!sig->is_builtin) + return true; + } + return false; +} + + +ir_call * +ir_call::get_error_instruction(void *ctx) +{ + ir_call *call = new(ctx) ir_call; + + call->type = glsl_type::error_type; + return call; +} + +void +ir_call::set_callee(ir_function_signature *sig) +{ + assert((this->type == NULL) || (this->type == sig->return_type)); + + this->callee = sig; +} + +void +visit_exec_list(exec_list *list, ir_visitor *visitor) +{ + foreach_iter(exec_list_iterator, iter, *list) { + ((ir_instruction *)iter.get())->accept(visitor); + } +} + + +static void +steal_memory(ir_instruction *ir, void *new_ctx) +{ + ir_variable *var = ir->as_variable(); + ir_constant *constant = ir->as_constant(); + if (var != NULL && var->constant_value != NULL) + steal_memory(var->constant_value, ir); + + /* The components of aggregate constants are not visited by the normal + * visitor, so steal their values by hand. + */ + if (constant != NULL) { + if (constant->type->is_record()) { + foreach_iter(exec_list_iterator, iter, constant->components) { + ir_constant *field = (ir_constant *)iter.get(); + steal_memory(field, ir); + } + } else if (constant->type->is_array()) { + for (unsigned int i = 0; i < constant->type->length; i++) { + steal_memory(constant->array_elements[i], ir); + } + } + } + + ralloc_steal(new_ctx, ir); +} + + +void +reparent_ir(exec_list *list, void *mem_ctx) +{ + foreach_list(node, list) { + visit_tree((ir_instruction *) node, steal_memory, mem_ctx); + } +} + + +static ir_rvalue * +try_min_one(ir_rvalue *ir) +{ + ir_expression *expr = ir->as_expression(); + + if (!expr || expr->operation != ir_binop_min) + return NULL; + + if (expr->operands[0]->is_one()) + return expr->operands[1]; + + if (expr->operands[1]->is_one()) + return expr->operands[0]; + + return NULL; +} + +static ir_rvalue * +try_max_zero(ir_rvalue *ir) +{ + ir_expression *expr = ir->as_expression(); + + if (!expr || expr->operation != ir_binop_max) + return NULL; + + if (expr->operands[0]->is_zero()) + return expr->operands[1]; + + if (expr->operands[1]->is_zero()) + return expr->operands[0]; + + return NULL; +} + +ir_rvalue * +ir_rvalue::as_rvalue_to_saturate() +{ + ir_expression *expr = this->as_expression(); + + if (!expr) + return NULL; + + ir_rvalue *max_zero = try_max_zero(expr); + if (max_zero) { + return try_min_one(max_zero); + } else { + ir_rvalue *min_one = try_min_one(expr); + if (min_one) { + return try_max_zero(min_one); + } + } + + return NULL; +} diff --git a/mesalib/src/glsl/ir.h b/mesalib/src/glsl/ir.h index 01008092a..2e899f3ed 100644 --- a/mesalib/src/glsl/ir.h +++ b/mesalib/src/glsl/ir.h @@ -1,1704 +1,1704 @@ -/* -*- c++ -*- */ -/* - * 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. - */ - -#pragma once -#ifndef IR_H -#define IR_H - -#include -#include - -#include "ralloc.h" -#include "glsl_types.h" -#include "list.h" -#include "ir_visitor.h" -#include "ir_hierarchical_visitor.h" - -/** - * \defgroup IR Intermediate representation nodes - * - * @{ - */ - -/** - * Class tags - * - * Each concrete class derived from \c ir_instruction has a value in this - * enumerant. The value for the type is stored in \c ir_instruction::ir_type - * by the constructor. While using type tags is not very C++, it is extremely - * convenient. For example, during debugging you can simply inspect - * \c ir_instruction::ir_type to find out the actual type of the object. - * - * In addition, it is possible to use a switch-statement based on \c - * \c ir_instruction::ir_type to select different behavior for different object - * types. For functions that have only slight differences for several object - * types, this allows writing very straightforward, readable code. - */ -enum ir_node_type { - /** - * Zero is unused so that the IR validator can detect cases where - * \c ir_instruction::ir_type has not been initialized. - */ - ir_type_unset, - ir_type_variable, - ir_type_assignment, - ir_type_call, - ir_type_constant, - ir_type_dereference_array, - ir_type_dereference_record, - ir_type_dereference_variable, - ir_type_discard, - ir_type_expression, - ir_type_function, - ir_type_function_signature, - ir_type_if, - ir_type_loop, - ir_type_loop_jump, - ir_type_return, - ir_type_swizzle, - ir_type_texture, - ir_type_max /**< maximum ir_type enum number, for validation */ -}; - -/** - * Base class of all IR instructions - */ -class ir_instruction : public exec_node { -public: - enum ir_node_type ir_type; - const struct glsl_type *type; - - /** ir_print_visitor helper for debugging. */ - void print(void) const; - - virtual void accept(ir_visitor *) = 0; - virtual ir_visitor_status accept(ir_hierarchical_visitor *) = 0; - virtual ir_instruction *clone(void *mem_ctx, - struct hash_table *ht) const = 0; - - /** - * \name IR instruction downcast functions - * - * These functions either cast the object to a derived class or return - * \c NULL if the object's type does not match the specified derived class. - * Additional downcast functions will be added as needed. - */ - /*@{*/ - virtual class ir_variable * as_variable() { return NULL; } - virtual class ir_function * as_function() { return NULL; } - virtual class ir_dereference * as_dereference() { return NULL; } - virtual class ir_dereference_array * as_dereference_array() { return NULL; } - virtual class ir_dereference_variable *as_dereference_variable() { return NULL; } - virtual class ir_expression * as_expression() { return NULL; } - virtual class ir_rvalue * as_rvalue() { return NULL; } - virtual class ir_loop * as_loop() { return NULL; } - virtual class ir_assignment * as_assignment() { return NULL; } - virtual class ir_call * as_call() { return NULL; } - virtual class ir_return * as_return() { return NULL; } - virtual class ir_if * as_if() { return NULL; } - virtual class ir_swizzle * as_swizzle() { return NULL; } - virtual class ir_constant * as_constant() { return NULL; } - virtual class ir_discard * as_discard() { return NULL; } - /*@}*/ - -protected: - ir_instruction() - { - ir_type = ir_type_unset; - type = NULL; - } -}; - - -class ir_rvalue : public ir_instruction { -public: - virtual ir_rvalue *clone(void *mem_ctx, struct hash_table *) const = 0; - - virtual ir_constant *constant_expression_value() = 0; - - virtual ir_rvalue * as_rvalue() - { - return this; - } - - ir_rvalue *as_rvalue_to_saturate(); - - virtual bool is_lvalue() const - { - return false; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const - { - return NULL; - } - - - /** - * If an r-value is a reference to a whole variable, get that variable - * - * \return - * Pointer to a variable that is completely dereferenced by the r-value. If - * the r-value is not a dereference or the dereference does not access the - * entire variable (i.e., it's just one array element, struct field), \c NULL - * is returned. - */ - virtual ir_variable *whole_variable_referenced() - { - return NULL; - } - - /** - * Determine if an r-value has the value zero - * - * The base implementation of this function always returns \c false. The - * \c ir_constant class over-rides this function to return \c true \b only - * for vector and scalar types that have all elements set to the value - * zero (or \c false for booleans). - * - * \sa ir_constant::has_value, ir_rvalue::is_one, ir_rvalue::is_negative_one - */ - virtual bool is_zero() const; - - /** - * Determine if an r-value has the value one - * - * The base implementation of this function always returns \c false. The - * \c ir_constant class over-rides this function to return \c true \b only - * for vector and scalar types that have all elements set to the value - * one (or \c true for booleans). - * - * \sa ir_constant::has_value, ir_rvalue::is_zero, ir_rvalue::is_negative_one - */ - virtual bool is_one() const; - - /** - * Determine if an r-value has the value negative one - * - * The base implementation of this function always returns \c false. The - * \c ir_constant class over-rides this function to return \c true \b only - * for vector and scalar types that have all elements set to the value - * negative one. For boolean times, the result is always \c false. - * - * \sa ir_constant::has_value, ir_rvalue::is_zero, ir_rvalue::is_one - */ - virtual bool is_negative_one() const; - -protected: - ir_rvalue(); -}; - - -/** - * Variable storage classes - */ -enum ir_variable_mode { - ir_var_auto = 0, /**< Function local variables and globals. */ - ir_var_uniform, /**< Variable declared as a uniform. */ - ir_var_in, - ir_var_out, - ir_var_inout, - ir_var_const_in, /**< "in" param that must be a constant expression */ - ir_var_system_value, /**< Ex: front-face, instance-id, etc. */ - ir_var_temporary /**< Temporary variable generated during compilation. */ -}; - -enum ir_variable_interpolation { - ir_var_smooth = 0, - ir_var_flat, - ir_var_noperspective -}; - -/** - * \brief Layout qualifiers for gl_FragDepth. - * - * The AMD/ARB_conservative_depth extensions allow gl_FragDepth to be redeclared - * with a layout qualifier. - */ -enum ir_depth_layout { - ir_depth_layout_none, /**< No depth layout is specified. */ - ir_depth_layout_any, - ir_depth_layout_greater, - ir_depth_layout_less, - ir_depth_layout_unchanged -}; - -/** - * \brief Convert depth layout qualifier to string. - */ -const char* -depth_layout_string(ir_depth_layout layout); - -/** - * Description of built-in state associated with a uniform - * - * \sa ir_variable::state_slots - */ -struct ir_state_slot { - int tokens[5]; - int swizzle; -}; - -class ir_variable : public ir_instruction { -public: - ir_variable(const struct glsl_type *, const char *, ir_variable_mode); - - virtual ir_variable *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual ir_variable *as_variable() - { - return this; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - - /** - * Get the string value for the interpolation qualifier - * - * \return The string that would be used in a shader to specify \c - * mode will be returned. - * - * This function should only be used on a shader input or output variable. - */ - const char *interpolation_string() const; - - /** - * Calculate the number of slots required to hold this variable - * - * This is used to determine how many uniform or varying locations a variable - * occupies. The count is in units of floating point components. - */ - unsigned component_slots() const; - - /** - * Delcared name of the variable - */ - const char *name; - - /** - * Highest element accessed with a constant expression array index - * - * Not used for non-array variables. - */ - unsigned max_array_access; - - /** - * Is the variable read-only? - * - * This is set for variables declared as \c const, shader inputs, - * and uniforms. - */ - unsigned read_only:1; - unsigned centroid:1; - unsigned invariant:1; - - /** - * Has this variable been used for reading or writing? - * - * Several GLSL semantic checks require knowledge of whether or not a - * variable has been used. For example, it is an error to redeclare a - * variable as invariant after it has been used. - */ - unsigned used:1; - - /** - * Storage class of the variable. - * - * \sa ir_variable_mode - */ - unsigned mode:3; - - /** - * Interpolation mode for shader inputs / outputs - * - * \sa ir_variable_interpolation - */ - unsigned interpolation:2; - - /** - * Flag that the whole array is assignable - * - * In GLSL 1.20 and later whole arrays are assignable (and comparable for - * equality). This flag enables this behavior. - */ - unsigned array_lvalue:1; - - /** - * \name ARB_fragment_coord_conventions - * @{ - */ - unsigned origin_upper_left:1; - unsigned pixel_center_integer:1; - /*@}*/ - - /** - * \brief Layout qualifier for gl_FragDepth. - * - * This is not equal to \c ir_depth_layout_none if and only if this - * variable is \c gl_FragDepth and a layout qualifier is specified. - */ - ir_depth_layout depth_layout; - - /** - * Was the location explicitly set in the shader? - * - * If the location is explicitly set in the shader, it \b cannot be changed - * by the linker or by the API (e.g., calls to \c glBindAttribLocation have - * no effect). - */ - unsigned explicit_location:1; - - /** - * Storage location of the base of this variable - * - * The precise meaning of this field depends on the nature of the variable. - * - * - Vertex shader input: one of the values from \c gl_vert_attrib. - * - Vertex shader output: one of the values from \c gl_vert_result. - * - Fragment shader input: one of the values from \c gl_frag_attrib. - * - Fragment shader output: one of the values from \c gl_frag_result. - * - Uniforms: Per-stage uniform slot number. - * - Other: This field is not currently used. - * - * If the variable is a uniform, shader input, or shader output, and the - * slot has not been assigned, the value will be -1. - */ - int location; - - /** - * Built-in state that backs this uniform - * - * Once set at variable creation, \c state_slots must remain invariant. - * This is because, ideally, this array would be shared by all clones of - * this variable in the IR tree. In other words, we'd really like for it - * to be a fly-weight. - * - * If the variable is not a uniform, \c num_state_slots will be zero and - * \c state_slots will be \c NULL. - */ - /*@{*/ - unsigned num_state_slots; /**< Number of state slots used */ - ir_state_slot *state_slots; /**< State descriptors. */ - /*@}*/ - - /** - * Emit a warning if this variable is accessed. - */ - const char *warn_extension; - - /** - * Value assigned in the initializer of a variable declared "const" - */ - ir_constant *constant_value; -}; - - -/*@{*/ -/** - * The representation of a function instance; may be the full definition or - * simply a prototype. - */ -class ir_function_signature : public ir_instruction { - /* An ir_function_signature will be part of the list of signatures in - * an ir_function. - */ -public: - ir_function_signature(const glsl_type *return_type); - - virtual ir_function_signature *clone(void *mem_ctx, - struct hash_table *ht) const; - ir_function_signature *clone_prototype(void *mem_ctx, - struct hash_table *ht) const; - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - /** - * Get the name of the function for which this is a signature - */ - const char *function_name() const; - - /** - * Get a handle to the function for which this is a signature - * - * There is no setter function, this function returns a \c const pointer, - * and \c ir_function_signature::_function is private for a reason. The - * only way to make a connection between a function and function signature - * is via \c ir_function::add_signature. This helps ensure that certain - * invariants (i.e., a function signature is in the list of signatures for - * its \c _function) are met. - * - * \sa ir_function::add_signature - */ - inline const class ir_function *function() const - { - return this->_function; - } - - /** - * Check whether the qualifiers match between this signature's parameters - * and the supplied parameter list. If not, returns the name of the first - * parameter with mismatched qualifiers (for use in error messages). - */ - const char *qualifiers_match(exec_list *params); - - /** - * Replace the current parameter list with the given one. This is useful - * if the current information came from a prototype, and either has invalid - * or missing parameter names. - */ - void replace_parameters(exec_list *new_params); - - /** - * Function return type. - * - * \note This discards the optional precision qualifier. - */ - const struct glsl_type *return_type; - - /** - * List of ir_variable of function parameters. - * - * This represents the storage. The paramaters passed in a particular - * call will be in ir_call::actual_paramaters. - */ - struct exec_list parameters; - - /** Whether or not this function has a body (which may be empty). */ - unsigned is_defined:1; - - /** Whether or not this function signature is a built-in. */ - unsigned is_builtin:1; - - /** Body of instructions in the function. */ - struct exec_list body; - -private: - /** Function of which this signature is one overload. */ - class ir_function *_function; - - friend class ir_function; -}; - - -/** - * Header for tracking multiple overloaded functions with the same name. - * Contains a list of ir_function_signatures representing each of the - * actual functions. - */ -class ir_function : public ir_instruction { -public: - ir_function(const char *name); - - virtual ir_function *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual ir_function *as_function() - { - return this; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - void add_signature(ir_function_signature *sig) - { - sig->_function = this; - this->signatures.push_tail(sig); - } - - /** - * Get an iterator for the set of function signatures - */ - exec_list_iterator iterator() - { - return signatures.iterator(); - } - - /** - * Find a signature that matches a set of actual parameters, taking implicit - * conversions into account. - */ - ir_function_signature *matching_signature(const exec_list *actual_param); - - /** - * Find a signature that exactly matches a set of actual parameters without - * any implicit type conversions. - */ - ir_function_signature *exact_matching_signature(const exec_list *actual_ps); - - /** - * Name of the function. - */ - const char *name; - - /** Whether or not this function has a signature that isn't a built-in. */ - bool has_user_signature(); - - /** - * List of ir_function_signature for each overloaded function with this name. - */ - struct exec_list signatures; -}; - -inline const char *ir_function_signature::function_name() const -{ - return this->_function->name; -} -/*@}*/ - - -/** - * IR instruction representing high-level if-statements - */ -class ir_if : public ir_instruction { -public: - ir_if(ir_rvalue *condition) - : condition(condition) - { - ir_type = ir_type_if; - } - - virtual ir_if *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual ir_if *as_if() - { - return this; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - ir_rvalue *condition; - /** List of ir_instruction for the body of the then branch */ - exec_list then_instructions; - /** List of ir_instruction for the body of the else branch */ - exec_list else_instructions; -}; - - -/** - * IR instruction representing a high-level loop structure. - */ -class ir_loop : public ir_instruction { -public: - ir_loop(); - - virtual ir_loop *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - virtual ir_loop *as_loop() - { - return this; - } - - /** - * Get an iterator for the instructions of the loop body - */ - exec_list_iterator iterator() - { - return body_instructions.iterator(); - } - - /** List of ir_instruction that make up the body of the loop. */ - exec_list body_instructions; - - /** - * \name Loop counter and controls - * - * Represents a loop like a FORTRAN \c do-loop. - * - * \note - * If \c from and \c to are the same value, the loop will execute once. - */ - /*@{*/ - ir_rvalue *from; /** Value of the loop counter on the first - * iteration of the loop. - */ - ir_rvalue *to; /** Value of the loop counter on the last - * iteration of the loop. - */ - ir_rvalue *increment; - ir_variable *counter; - - /** - * Comparison operation in the loop terminator. - * - * If any of the loop control fields are non-\c NULL, this field must be - * one of \c ir_binop_less, \c ir_binop_greater, \c ir_binop_lequal, - * \c ir_binop_gequal, \c ir_binop_equal, or \c ir_binop_nequal. - */ - int cmp; - /*@}*/ -}; - - -class ir_assignment : public ir_instruction { -public: - ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, ir_rvalue *condition = NULL); - - /** - * Construct an assignment with an explicit write mask - * - * \note - * Since a write mask is supplied, the LHS must already be a bare - * \c ir_dereference. The cannot be any swizzles in the LHS. - */ - ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, ir_rvalue *condition, - unsigned write_mask); - - virtual ir_assignment *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual ir_constant *constant_expression_value(); - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - virtual ir_assignment * as_assignment() - { - return this; - } - - /** - * Get a whole variable written by an assignment - * - * If the LHS of the assignment writes a whole variable, the variable is - * returned. Otherwise \c NULL is returned. Examples of whole-variable - * assignment are: - * - * - Assigning to a scalar - * - Assigning to all components of a vector - * - Whole array (or matrix) assignment - * - Whole structure assignment - */ - ir_variable *whole_variable_written(); - - /** - * Set the LHS of an assignment - */ - void set_lhs(ir_rvalue *lhs); - - /** - * Left-hand side of the assignment. - * - * This should be treated as read only. If you need to set the LHS of an - * assignment, use \c ir_assignment::set_lhs. - */ - ir_dereference *lhs; - - /** - * Value being assigned - */ - ir_rvalue *rhs; - - /** - * Optional condition for the assignment. - */ - ir_rvalue *condition; - - - /** - * Component mask written - * - * For non-vector types in the LHS, this field will be zero. For vector - * types, a bit will be set for each component that is written. Note that - * for \c vec2 and \c vec3 types only the lower bits will ever be set. - * - * A partially-set write mask means that each enabled channel gets - * the value from a consecutive channel of the rhs. For example, - * to write just .xyw of gl_FrontColor with color: - * - * (assign (constant bool (1)) (xyw) - * (var_ref gl_FragColor) - * (swiz xyw (var_ref color))) - */ - unsigned write_mask:4; -}; - -/* Update ir_expression::num_operands() and operator_strs when - * updating this list. - */ -enum ir_expression_operation { - ir_unop_bit_not, - ir_unop_logic_not, - ir_unop_neg, - ir_unop_abs, - ir_unop_sign, - ir_unop_rcp, - ir_unop_rsq, - ir_unop_sqrt, - ir_unop_exp, /**< Log base e on gentype */ - ir_unop_log, /**< Natural log on gentype */ - ir_unop_exp2, - ir_unop_log2, - ir_unop_f2i, /**< Float-to-integer conversion. */ - ir_unop_i2f, /**< Integer-to-float conversion. */ - ir_unop_f2b, /**< Float-to-boolean conversion */ - ir_unop_b2f, /**< Boolean-to-float conversion */ - ir_unop_i2b, /**< int-to-boolean conversion */ - ir_unop_b2i, /**< Boolean-to-int conversion */ - ir_unop_u2f, /**< Unsigned-to-float conversion. */ - ir_unop_i2u, /**< Integer-to-unsigned conversion. */ - ir_unop_u2i, /**< Unsigned-to-integer conversion. */ - ir_unop_any, - - /** - * \name Unary floating-point rounding operations. - */ - /*@{*/ - ir_unop_trunc, - ir_unop_ceil, - ir_unop_floor, - ir_unop_fract, - ir_unop_round_even, - /*@}*/ - - /** - * \name Trigonometric operations. - */ - /*@{*/ - ir_unop_sin, - ir_unop_cos, - ir_unop_sin_reduced, /**< Reduced range sin. [-pi, pi] */ - ir_unop_cos_reduced, /**< Reduced range cos. [-pi, pi] */ - /*@}*/ - - /** - * \name Partial derivatives. - */ - /*@{*/ - ir_unop_dFdx, - ir_unop_dFdy, - /*@}*/ - - ir_unop_noise, - - /** - * A sentinel marking the last of the unary operations. - */ - ir_last_unop = ir_unop_noise, - - ir_binop_add, - ir_binop_sub, - ir_binop_mul, - ir_binop_div, - - /** - * Takes one of two combinations of arguments: - * - * - mod(vecN, vecN) - * - mod(vecN, float) - * - * Does not take integer types. - */ - ir_binop_mod, - - /** - * \name Binary comparison operators which return a boolean vector. - * The type of both operands must be equal. - */ - /*@{*/ - ir_binop_less, - ir_binop_greater, - ir_binop_lequal, - ir_binop_gequal, - ir_binop_equal, - ir_binop_nequal, - /** - * Returns single boolean for whether all components of operands[0] - * equal the components of operands[1]. - */ - ir_binop_all_equal, - /** - * Returns single boolean for whether any component of operands[0] - * is not equal to the corresponding component of operands[1]. - */ - ir_binop_any_nequal, - /*@}*/ - - /** - * \name Bit-wise binary operations. - */ - /*@{*/ - ir_binop_lshift, - ir_binop_rshift, - ir_binop_bit_and, - ir_binop_bit_xor, - ir_binop_bit_or, - /*@}*/ - - ir_binop_logic_and, - ir_binop_logic_xor, - ir_binop_logic_or, - - ir_binop_dot, - ir_binop_min, - ir_binop_max, - - ir_binop_pow, - - /** - * A sentinel marking the last of the binary operations. - */ - ir_last_binop = ir_binop_pow, - - ir_quadop_vector, - - /** - * A sentinel marking the last of all operations. - */ - ir_last_opcode = ir_last_binop -}; - -class ir_expression : public ir_rvalue { -public: - /** - * Constructor for unary operation expressions - */ - ir_expression(int op, const struct glsl_type *type, ir_rvalue *); - ir_expression(int op, ir_rvalue *); - - /** - * Constructor for binary operation expressions - */ - ir_expression(int op, const struct glsl_type *type, - ir_rvalue *, ir_rvalue *); - ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1); - - /** - * Constructor for quad operator expressions - */ - ir_expression(int op, const struct glsl_type *type, - ir_rvalue *, ir_rvalue *, ir_rvalue *, ir_rvalue *); - - virtual ir_expression *as_expression() - { - return this; - } - - virtual ir_expression *clone(void *mem_ctx, struct hash_table *ht) const; - - /** - * Attempt to constant-fold the expression - * - * If the expression cannot be constant folded, this method will return - * \c NULL. - */ - virtual ir_constant *constant_expression_value(); - - /** - * Determine the number of operands used by an expression - */ - static unsigned int get_num_operands(ir_expression_operation); - - /** - * Determine the number of operands used by an expression - */ - unsigned int get_num_operands() const - { - return (this->operation == ir_quadop_vector) - ? this->type->vector_elements : get_num_operands(operation); - } - - /** - * Return a string representing this expression's operator. - */ - const char *operator_string(); - - /** - * Return a string representing this expression's operator. - */ - static const char *operator_string(ir_expression_operation); - - - /** - * Do a reverse-lookup to translate the given string into an operator. - */ - static ir_expression_operation get_operator(const char *); - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - ir_expression_operation operation; - ir_rvalue *operands[4]; -}; - - -/** - * IR instruction representing a function call - */ -class ir_call : public ir_rvalue { -public: - ir_call(ir_function_signature *callee, exec_list *actual_parameters) - : callee(callee) - { - ir_type = ir_type_call; - assert(callee->return_type != NULL); - type = callee->return_type; - actual_parameters->move_nodes_to(& this->actual_parameters); - this->use_builtin = callee->is_builtin; - } - - virtual ir_call *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual ir_constant *constant_expression_value(); - - virtual ir_call *as_call() - { - return this; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - /** - * Get a generic ir_call object when an error occurs - * - * Any allocation will be performed with 'ctx' as ralloc owner. - */ - static ir_call *get_error_instruction(void *ctx); - - /** - * Get an iterator for the set of acutal parameters - */ - exec_list_iterator iterator() - { - return actual_parameters.iterator(); - } - - /** - * Get the name of the function being called. - */ - const char *callee_name() const - { - return callee->function_name(); - } - - /** - * Get the function signature bound to this function call - */ - ir_function_signature *get_callee() - { - return callee; - } - - /** - * Set the function call target - */ - void set_callee(ir_function_signature *sig); - - /** - * Generates an inline version of the function before @ir, - * returning the return value of the function. - */ - ir_rvalue *generate_inline(ir_instruction *ir); - - /* List of ir_rvalue of paramaters passed in this call. */ - exec_list actual_parameters; - - /** Should this call only bind to a built-in function? */ - bool use_builtin; - -private: - ir_call() - : callee(NULL) - { - this->ir_type = ir_type_call; - } - - ir_function_signature *callee; -}; - - -/** - * \name Jump-like IR instructions. - * - * These include \c break, \c continue, \c return, and \c discard. - */ -/*@{*/ -class ir_jump : public ir_instruction { -protected: - ir_jump() - { - ir_type = ir_type_unset; - } -}; - -class ir_return : public ir_jump { -public: - ir_return() - : value(NULL) - { - this->ir_type = ir_type_return; - } - - ir_return(ir_rvalue *value) - : value(value) - { - this->ir_type = ir_type_return; - } - - virtual ir_return *clone(void *mem_ctx, struct hash_table *) const; - - virtual ir_return *as_return() - { - return this; - } - - ir_rvalue *get_value() const - { - return value; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - ir_rvalue *value; -}; - - -/** - * Jump instructions used inside loops - * - * These include \c break and \c continue. The \c break within a loop is - * different from the \c break within a switch-statement. - * - * \sa ir_switch_jump - */ -class ir_loop_jump : public ir_jump { -public: - enum jump_mode { - jump_break, - jump_continue - }; - - ir_loop_jump(jump_mode mode) - { - this->ir_type = ir_type_loop_jump; - this->mode = mode; - this->loop = loop; - } - - virtual ir_loop_jump *clone(void *mem_ctx, struct hash_table *) const; - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - bool is_break() const - { - return mode == jump_break; - } - - bool is_continue() const - { - return mode == jump_continue; - } - - /** Mode selector for the jump instruction. */ - enum jump_mode mode; -private: - /** Loop containing this break instruction. */ - ir_loop *loop; -}; - -/** - * IR instruction representing discard statements. - */ -class ir_discard : public ir_jump { -public: - ir_discard() - { - this->ir_type = ir_type_discard; - this->condition = NULL; - } - - ir_discard(ir_rvalue *cond) - { - this->ir_type = ir_type_discard; - this->condition = cond; - } - - virtual ir_discard *clone(void *mem_ctx, struct hash_table *ht) const; - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - virtual ir_discard *as_discard() - { - return this; - } - - ir_rvalue *condition; -}; -/*@}*/ - - -/** - * Texture sampling opcodes used in ir_texture - */ -enum ir_texture_opcode { - ir_tex, /**< Regular texture look-up */ - ir_txb, /**< Texture look-up with LOD bias */ - ir_txl, /**< Texture look-up with explicit LOD */ - ir_txd, /**< Texture look-up with partial derivatvies */ - ir_txf, /**< Texel fetch with explicit LOD */ - ir_txs /**< Texture size */ -}; - - -/** - * IR instruction to sample a texture - * - * The specific form of the IR instruction depends on the \c mode value - * selected from \c ir_texture_opcodes. In the printed IR, these will - * appear as: - * - * Texel offset (0 or an expression) - * | Projection divisor - * | | Shadow comparitor - * | | | - * v v v - * (tex 0 1 ( )) - * (txb 0 1 ( ) ) - * (txl 0 1 ( ) ) - * (txd 0 1 ( ) (dPdx dPdy)) - * (txf 0 ) - * (txs ) - */ -class ir_texture : public ir_rvalue { -public: - ir_texture(enum ir_texture_opcode op) - : op(op), projector(NULL), shadow_comparitor(NULL), offset(NULL) - { - this->ir_type = ir_type_texture; - } - - virtual ir_texture *clone(void *mem_ctx, struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - /** - * Return a string representing the ir_texture_opcode. - */ - const char *opcode_string(); - - /** Set the sampler and type. */ - void set_sampler(ir_dereference *sampler, const glsl_type *type); - - /** - * Do a reverse-lookup to translate a string into an ir_texture_opcode. - */ - static ir_texture_opcode get_opcode(const char *); - - enum ir_texture_opcode op; - - /** Sampler to use for the texture access. */ - ir_dereference *sampler; - - /** Texture coordinate to sample */ - ir_rvalue *coordinate; - - /** - * Value used for projective divide. - * - * If there is no projective divide (the common case), this will be - * \c NULL. Optimization passes should check for this to point to a constant - * of 1.0 and replace that with \c NULL. - */ - ir_rvalue *projector; - - /** - * Coordinate used for comparison on shadow look-ups. - * - * If there is no shadow comparison, this will be \c NULL. For the - * \c ir_txf opcode, this *must* be \c NULL. - */ - ir_rvalue *shadow_comparitor; - - /** Texel offset. */ - ir_rvalue *offset; - - union { - ir_rvalue *lod; /**< Floating point LOD */ - ir_rvalue *bias; /**< Floating point LOD bias */ - struct { - ir_rvalue *dPdx; /**< Partial derivative of coordinate wrt X */ - ir_rvalue *dPdy; /**< Partial derivative of coordinate wrt Y */ - } grad; - } lod_info; -}; - - -struct ir_swizzle_mask { - unsigned x:2; - unsigned y:2; - unsigned z:2; - unsigned w:2; - - /** - * Number of components in the swizzle. - */ - unsigned num_components:3; - - /** - * Does the swizzle contain duplicate components? - * - * L-value swizzles cannot contain duplicate components. - */ - unsigned has_duplicates:1; -}; - - -class ir_swizzle : public ir_rvalue { -public: - ir_swizzle(ir_rvalue *, unsigned x, unsigned y, unsigned z, unsigned w, - unsigned count); - - ir_swizzle(ir_rvalue *val, const unsigned *components, unsigned count); - - ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask); - - virtual ir_swizzle *clone(void *mem_ctx, struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - virtual ir_swizzle *as_swizzle() - { - return this; - } - - /** - * Construct an ir_swizzle from the textual representation. Can fail. - */ - static ir_swizzle *create(ir_rvalue *, const char *, unsigned vector_length); - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - bool is_lvalue() const - { - return val->is_lvalue() && !mask.has_duplicates; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const; - - ir_rvalue *val; - ir_swizzle_mask mask; - -private: - /** - * Initialize the mask component of a swizzle - * - * This is used by the \c ir_swizzle constructors. - */ - void init_mask(const unsigned *components, unsigned count); -}; - - -class ir_dereference : public ir_rvalue { -public: - virtual ir_dereference *clone(void *mem_ctx, struct hash_table *) const = 0; - - virtual ir_dereference *as_dereference() - { - return this; - } - - bool is_lvalue() const; - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const = 0; -}; - - -class ir_dereference_variable : public ir_dereference { -public: - ir_dereference_variable(ir_variable *var); - - virtual ir_dereference_variable *clone(void *mem_ctx, - struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - virtual ir_dereference_variable *as_dereference_variable() - { - return this; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const - { - return this->var; - } - - virtual ir_variable *whole_variable_referenced() - { - /* ir_dereference_variable objects always dereference the entire - * variable. However, if this dereference is dereferenced by anything - * else, the complete deferefernce chain is not a whole-variable - * dereference. This method should only be called on the top most - * ir_rvalue in a dereference chain. - */ - return this->var; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - /** - * Object being dereferenced. - */ - ir_variable *var; -}; - - -class ir_dereference_array : public ir_dereference { -public: - ir_dereference_array(ir_rvalue *value, ir_rvalue *array_index); - - ir_dereference_array(ir_variable *var, ir_rvalue *array_index); - - virtual ir_dereference_array *clone(void *mem_ctx, - struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - virtual ir_dereference_array *as_dereference_array() - { - return this; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const - { - return this->array->variable_referenced(); - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - ir_rvalue *array; - ir_rvalue *array_index; - -private: - void set_array(ir_rvalue *value); -}; - - -class ir_dereference_record : public ir_dereference { -public: - ir_dereference_record(ir_rvalue *value, const char *field); - - ir_dereference_record(ir_variable *var, const char *field); - - virtual ir_dereference_record *clone(void *mem_ctx, - struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() const - { - return this->record->variable_referenced(); - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - ir_rvalue *record; - const char *field; -}; - - -/** - * Data stored in an ir_constant - */ -union ir_constant_data { - unsigned u[16]; - int i[16]; - float f[16]; - bool b[16]; -}; - - -class ir_constant : public ir_rvalue { -public: - ir_constant(const struct glsl_type *type, const ir_constant_data *data); - ir_constant(bool b); - ir_constant(unsigned int u); - ir_constant(int i); - ir_constant(float f); - - /** - * Construct an ir_constant from a list of ir_constant values - */ - ir_constant(const struct glsl_type *type, exec_list *values); - - /** - * Construct an ir_constant from a scalar component of another ir_constant - * - * The new \c ir_constant inherits the type of the component from the - * source constant. - * - * \note - * In the case of a matrix constant, the new constant is a scalar, \b not - * a vector. - */ - ir_constant(const ir_constant *c, unsigned i); - - /** - * Return a new ir_constant of the specified type containing all zeros. - */ - static ir_constant *zero(void *mem_ctx, const glsl_type *type); - - virtual ir_constant *clone(void *mem_ctx, struct hash_table *) const; - - virtual ir_constant *constant_expression_value(); - - virtual ir_constant *as_constant() - { - return this; - } - - virtual void accept(ir_visitor *v) - { - v->visit(this); - } - - virtual ir_visitor_status accept(ir_hierarchical_visitor *); - - /** - * Get a particular component of a constant as a specific type - * - * This is useful, for example, to get a value from an integer constant - * as a float or bool. This appears frequently when constructors are - * called with all constant parameters. - */ - /*@{*/ - bool get_bool_component(unsigned i) const; - float get_float_component(unsigned i) const; - int get_int_component(unsigned i) const; - unsigned get_uint_component(unsigned i) const; - /*@}*/ - - ir_constant *get_array_element(unsigned i) const; - - ir_constant *get_record_field(const char *name); - - /** - * Determine whether a constant has the same value as another constant - * - * \sa ir_constant::is_zero, ir_constant::is_one, - * ir_constant::is_negative_one - */ - bool has_value(const ir_constant *) const; - - virtual bool is_zero() const; - virtual bool is_one() const; - virtual bool is_negative_one() const; - - /** - * Value of the constant. - * - * The field used to back the values supplied by the constant is determined - * by the type associated with the \c ir_instruction. Constants may be - * scalars, vectors, or matrices. - */ - union ir_constant_data value; - - /* Array elements */ - ir_constant **array_elements; - - /* Structure fields */ - exec_list components; - -private: - /** - * Parameterless constructor only used by the clone method - */ - ir_constant(void); -}; - -/*@}*/ - -/** - * Apply a visitor to each IR node in a list - */ -void -visit_exec_list(exec_list *list, ir_visitor *visitor); - -/** - * Validate invariants on each IR node in a list - */ -void validate_ir_tree(exec_list *instructions); - -struct _mesa_glsl_parse_state; -struct gl_shader_program; - -/** - * Detect whether an unlinked shader contains static recursion - * - * If the list of instructions is determined to contain static recursion, - * \c _mesa_glsl_error will be called to emit error messages for each function - * that is in the recursion cycle. - */ -void -detect_recursion_unlinked(struct _mesa_glsl_parse_state *state, - exec_list *instructions); - -/** - * Detect whether a linked shader contains static recursion - * - * If the list of instructions is determined to contain static recursion, - * \c link_error_printf will be called to emit error messages for each function - * that is in the recursion cycle. In addition, - * \c gl_shader_program::LinkStatus will be set to false. - */ -void -detect_recursion_linked(struct gl_shader_program *prog, - exec_list *instructions); - -/** - * Make a clone of each IR instruction in a list - * - * \param in List of IR instructions that are to be cloned - * \param out List to hold the cloned instructions - */ -void -clone_ir_list(void *mem_ctx, exec_list *out, const exec_list *in); - -extern void -_mesa_glsl_initialize_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state); - -extern void -_mesa_glsl_initialize_functions(_mesa_glsl_parse_state *state); - -extern void -_mesa_glsl_release_functions(void); - -extern void -reparent_ir(exec_list *list, void *mem_ctx); - -struct glsl_symbol_table; - -extern void -import_prototypes(const exec_list *source, exec_list *dest, - struct glsl_symbol_table *symbols, void *mem_ctx); - -extern bool -ir_has_call(ir_instruction *ir); - -extern void -do_set_program_inouts(exec_list *instructions, struct gl_program *prog); - -extern char * -prototype_string(const glsl_type *return_type, const char *name, - exec_list *parameters); - -#endif /* IR_H */ +/* -*- c++ -*- */ +/* + * 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. + */ + +#pragma once +#ifndef IR_H +#define IR_H + +#include +#include + +#include "ralloc.h" +#include "glsl_types.h" +#include "list.h" +#include "ir_visitor.h" +#include "ir_hierarchical_visitor.h" + +/** + * \defgroup IR Intermediate representation nodes + * + * @{ + */ + +/** + * Class tags + * + * Each concrete class derived from \c ir_instruction has a value in this + * enumerant. The value for the type is stored in \c ir_instruction::ir_type + * by the constructor. While using type tags is not very C++, it is extremely + * convenient. For example, during debugging you can simply inspect + * \c ir_instruction::ir_type to find out the actual type of the object. + * + * In addition, it is possible to use a switch-statement based on \c + * \c ir_instruction::ir_type to select different behavior for different object + * types. For functions that have only slight differences for several object + * types, this allows writing very straightforward, readable code. + */ +enum ir_node_type { + /** + * Zero is unused so that the IR validator can detect cases where + * \c ir_instruction::ir_type has not been initialized. + */ + ir_type_unset, + ir_type_variable, + ir_type_assignment, + ir_type_call, + ir_type_constant, + ir_type_dereference_array, + ir_type_dereference_record, + ir_type_dereference_variable, + ir_type_discard, + ir_type_expression, + ir_type_function, + ir_type_function_signature, + ir_type_if, + ir_type_loop, + ir_type_loop_jump, + ir_type_return, + ir_type_swizzle, + ir_type_texture, + ir_type_max /**< maximum ir_type enum number, for validation */ +}; + +/** + * Base class of all IR instructions + */ +class ir_instruction : public exec_node { +public: + enum ir_node_type ir_type; + const struct glsl_type *type; + + /** ir_print_visitor helper for debugging. */ + void print(void) const; + + virtual void accept(ir_visitor *) = 0; + virtual ir_visitor_status accept(ir_hierarchical_visitor *) = 0; + virtual ir_instruction *clone(void *mem_ctx, + struct hash_table *ht) const = 0; + + /** + * \name IR instruction downcast functions + * + * These functions either cast the object to a derived class or return + * \c NULL if the object's type does not match the specified derived class. + * Additional downcast functions will be added as needed. + */ + /*@{*/ + virtual class ir_variable * as_variable() { return NULL; } + virtual class ir_function * as_function() { return NULL; } + virtual class ir_dereference * as_dereference() { return NULL; } + virtual class ir_dereference_array * as_dereference_array() { return NULL; } + virtual class ir_dereference_variable *as_dereference_variable() { return NULL; } + virtual class ir_expression * as_expression() { return NULL; } + virtual class ir_rvalue * as_rvalue() { return NULL; } + virtual class ir_loop * as_loop() { return NULL; } + virtual class ir_assignment * as_assignment() { return NULL; } + virtual class ir_call * as_call() { return NULL; } + virtual class ir_return * as_return() { return NULL; } + virtual class ir_if * as_if() { return NULL; } + virtual class ir_swizzle * as_swizzle() { return NULL; } + virtual class ir_constant * as_constant() { return NULL; } + virtual class ir_discard * as_discard() { return NULL; } + /*@}*/ + +protected: + ir_instruction() + { + ir_type = ir_type_unset; + type = NULL; + } +}; + + +class ir_rvalue : public ir_instruction { +public: + virtual ir_rvalue *clone(void *mem_ctx, struct hash_table *) const = 0; + + virtual ir_constant *constant_expression_value() = 0; + + virtual ir_rvalue * as_rvalue() + { + return this; + } + + ir_rvalue *as_rvalue_to_saturate(); + + virtual bool is_lvalue() const + { + return false; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const + { + return NULL; + } + + + /** + * If an r-value is a reference to a whole variable, get that variable + * + * \return + * Pointer to a variable that is completely dereferenced by the r-value. If + * the r-value is not a dereference or the dereference does not access the + * entire variable (i.e., it's just one array element, struct field), \c NULL + * is returned. + */ + virtual ir_variable *whole_variable_referenced() + { + return NULL; + } + + /** + * Determine if an r-value has the value zero + * + * The base implementation of this function always returns \c false. The + * \c ir_constant class over-rides this function to return \c true \b only + * for vector and scalar types that have all elements set to the value + * zero (or \c false for booleans). + * + * \sa ir_constant::has_value, ir_rvalue::is_one, ir_rvalue::is_negative_one + */ + virtual bool is_zero() const; + + /** + * Determine if an r-value has the value one + * + * The base implementation of this function always returns \c false. The + * \c ir_constant class over-rides this function to return \c true \b only + * for vector and scalar types that have all elements set to the value + * one (or \c true for booleans). + * + * \sa ir_constant::has_value, ir_rvalue::is_zero, ir_rvalue::is_negative_one + */ + virtual bool is_one() const; + + /** + * Determine if an r-value has the value negative one + * + * The base implementation of this function always returns \c false. The + * \c ir_constant class over-rides this function to return \c true \b only + * for vector and scalar types that have all elements set to the value + * negative one. For boolean times, the result is always \c false. + * + * \sa ir_constant::has_value, ir_rvalue::is_zero, ir_rvalue::is_one + */ + virtual bool is_negative_one() const; + +protected: + ir_rvalue(); +}; + + +/** + * Variable storage classes + */ +enum ir_variable_mode { + ir_var_auto = 0, /**< Function local variables and globals. */ + ir_var_uniform, /**< Variable declared as a uniform. */ + ir_var_in, + ir_var_out, + ir_var_inout, + ir_var_const_in, /**< "in" param that must be a constant expression */ + ir_var_system_value, /**< Ex: front-face, instance-id, etc. */ + ir_var_temporary /**< Temporary variable generated during compilation. */ +}; + +enum ir_variable_interpolation { + ir_var_smooth = 0, + ir_var_flat, + ir_var_noperspective +}; + +/** + * \brief Layout qualifiers for gl_FragDepth. + * + * The AMD/ARB_conservative_depth extensions allow gl_FragDepth to be redeclared + * with a layout qualifier. + */ +enum ir_depth_layout { + ir_depth_layout_none, /**< No depth layout is specified. */ + ir_depth_layout_any, + ir_depth_layout_greater, + ir_depth_layout_less, + ir_depth_layout_unchanged +}; + +/** + * \brief Convert depth layout qualifier to string. + */ +const char* +depth_layout_string(ir_depth_layout layout); + +/** + * Description of built-in state associated with a uniform + * + * \sa ir_variable::state_slots + */ +struct ir_state_slot { + int tokens[5]; + int swizzle; +}; + +class ir_variable : public ir_instruction { +public: + ir_variable(const struct glsl_type *, const char *, ir_variable_mode); + + virtual ir_variable *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual ir_variable *as_variable() + { + return this; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + + /** + * Get the string value for the interpolation qualifier + * + * \return The string that would be used in a shader to specify \c + * mode will be returned. + * + * This function should only be used on a shader input or output variable. + */ + const char *interpolation_string() const; + + /** + * Calculate the number of slots required to hold this variable + * + * This is used to determine how many uniform or varying locations a variable + * occupies. The count is in units of floating point components. + */ + unsigned component_slots() const; + + /** + * Delcared name of the variable + */ + const char *name; + + /** + * Highest element accessed with a constant expression array index + * + * Not used for non-array variables. + */ + unsigned max_array_access; + + /** + * Is the variable read-only? + * + * This is set for variables declared as \c const, shader inputs, + * and uniforms. + */ + unsigned read_only:1; + unsigned centroid:1; + unsigned invariant:1; + + /** + * Has this variable been used for reading or writing? + * + * Several GLSL semantic checks require knowledge of whether or not a + * variable has been used. For example, it is an error to redeclare a + * variable as invariant after it has been used. + */ + unsigned used:1; + + /** + * Storage class of the variable. + * + * \sa ir_variable_mode + */ + unsigned mode:3; + + /** + * Interpolation mode for shader inputs / outputs + * + * \sa ir_variable_interpolation + */ + unsigned interpolation:2; + + /** + * Flag that the whole array is assignable + * + * In GLSL 1.20 and later whole arrays are assignable (and comparable for + * equality). This flag enables this behavior. + */ + unsigned array_lvalue:1; + + /** + * \name ARB_fragment_coord_conventions + * @{ + */ + unsigned origin_upper_left:1; + unsigned pixel_center_integer:1; + /*@}*/ + + /** + * \brief Layout qualifier for gl_FragDepth. + * + * This is not equal to \c ir_depth_layout_none if and only if this + * variable is \c gl_FragDepth and a layout qualifier is specified. + */ + ir_depth_layout depth_layout; + + /** + * Was the location explicitly set in the shader? + * + * If the location is explicitly set in the shader, it \b cannot be changed + * by the linker or by the API (e.g., calls to \c glBindAttribLocation have + * no effect). + */ + unsigned explicit_location:1; + + /** + * Storage location of the base of this variable + * + * The precise meaning of this field depends on the nature of the variable. + * + * - Vertex shader input: one of the values from \c gl_vert_attrib. + * - Vertex shader output: one of the values from \c gl_vert_result. + * - Fragment shader input: one of the values from \c gl_frag_attrib. + * - Fragment shader output: one of the values from \c gl_frag_result. + * - Uniforms: Per-stage uniform slot number. + * - Other: This field is not currently used. + * + * If the variable is a uniform, shader input, or shader output, and the + * slot has not been assigned, the value will be -1. + */ + int location; + + /** + * Built-in state that backs this uniform + * + * Once set at variable creation, \c state_slots must remain invariant. + * This is because, ideally, this array would be shared by all clones of + * this variable in the IR tree. In other words, we'd really like for it + * to be a fly-weight. + * + * If the variable is not a uniform, \c num_state_slots will be zero and + * \c state_slots will be \c NULL. + */ + /*@{*/ + unsigned num_state_slots; /**< Number of state slots used */ + ir_state_slot *state_slots; /**< State descriptors. */ + /*@}*/ + + /** + * Emit a warning if this variable is accessed. + */ + const char *warn_extension; + + /** + * Value assigned in the initializer of a variable declared "const" + */ + ir_constant *constant_value; +}; + + +/*@{*/ +/** + * The representation of a function instance; may be the full definition or + * simply a prototype. + */ +class ir_function_signature : public ir_instruction { + /* An ir_function_signature will be part of the list of signatures in + * an ir_function. + */ +public: + ir_function_signature(const glsl_type *return_type); + + virtual ir_function_signature *clone(void *mem_ctx, + struct hash_table *ht) const; + ir_function_signature *clone_prototype(void *mem_ctx, + struct hash_table *ht) const; + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + /** + * Get the name of the function for which this is a signature + */ + const char *function_name() const; + + /** + * Get a handle to the function for which this is a signature + * + * There is no setter function, this function returns a \c const pointer, + * and \c ir_function_signature::_function is private for a reason. The + * only way to make a connection between a function and function signature + * is via \c ir_function::add_signature. This helps ensure that certain + * invariants (i.e., a function signature is in the list of signatures for + * its \c _function) are met. + * + * \sa ir_function::add_signature + */ + inline const class ir_function *function() const + { + return this->_function; + } + + /** + * Check whether the qualifiers match between this signature's parameters + * and the supplied parameter list. If not, returns the name of the first + * parameter with mismatched qualifiers (for use in error messages). + */ + const char *qualifiers_match(exec_list *params); + + /** + * Replace the current parameter list with the given one. This is useful + * if the current information came from a prototype, and either has invalid + * or missing parameter names. + */ + void replace_parameters(exec_list *new_params); + + /** + * Function return type. + * + * \note This discards the optional precision qualifier. + */ + const struct glsl_type *return_type; + + /** + * List of ir_variable of function parameters. + * + * This represents the storage. The paramaters passed in a particular + * call will be in ir_call::actual_paramaters. + */ + struct exec_list parameters; + + /** Whether or not this function has a body (which may be empty). */ + unsigned is_defined:1; + + /** Whether or not this function signature is a built-in. */ + unsigned is_builtin:1; + + /** Body of instructions in the function. */ + struct exec_list body; + +private: + /** Function of which this signature is one overload. */ + class ir_function *_function; + + friend class ir_function; +}; + + +/** + * Header for tracking multiple overloaded functions with the same name. + * Contains a list of ir_function_signatures representing each of the + * actual functions. + */ +class ir_function : public ir_instruction { +public: + ir_function(const char *name); + + virtual ir_function *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual ir_function *as_function() + { + return this; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + void add_signature(ir_function_signature *sig) + { + sig->_function = this; + this->signatures.push_tail(sig); + } + + /** + * Get an iterator for the set of function signatures + */ + exec_list_iterator iterator() + { + return signatures.iterator(); + } + + /** + * Find a signature that matches a set of actual parameters, taking implicit + * conversions into account. + */ + ir_function_signature *matching_signature(const exec_list *actual_param); + + /** + * Find a signature that exactly matches a set of actual parameters without + * any implicit type conversions. + */ + ir_function_signature *exact_matching_signature(const exec_list *actual_ps); + + /** + * Name of the function. + */ + const char *name; + + /** Whether or not this function has a signature that isn't a built-in. */ + bool has_user_signature(); + + /** + * List of ir_function_signature for each overloaded function with this name. + */ + struct exec_list signatures; +}; + +inline const char *ir_function_signature::function_name() const +{ + return this->_function->name; +} +/*@}*/ + + +/** + * IR instruction representing high-level if-statements + */ +class ir_if : public ir_instruction { +public: + ir_if(ir_rvalue *condition) + : condition(condition) + { + ir_type = ir_type_if; + } + + virtual ir_if *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual ir_if *as_if() + { + return this; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + ir_rvalue *condition; + /** List of ir_instruction for the body of the then branch */ + exec_list then_instructions; + /** List of ir_instruction for the body of the else branch */ + exec_list else_instructions; +}; + + +/** + * IR instruction representing a high-level loop structure. + */ +class ir_loop : public ir_instruction { +public: + ir_loop(); + + virtual ir_loop *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + virtual ir_loop *as_loop() + { + return this; + } + + /** + * Get an iterator for the instructions of the loop body + */ + exec_list_iterator iterator() + { + return body_instructions.iterator(); + } + + /** List of ir_instruction that make up the body of the loop. */ + exec_list body_instructions; + + /** + * \name Loop counter and controls + * + * Represents a loop like a FORTRAN \c do-loop. + * + * \note + * If \c from and \c to are the same value, the loop will execute once. + */ + /*@{*/ + ir_rvalue *from; /** Value of the loop counter on the first + * iteration of the loop. + */ + ir_rvalue *to; /** Value of the loop counter on the last + * iteration of the loop. + */ + ir_rvalue *increment; + ir_variable *counter; + + /** + * Comparison operation in the loop terminator. + * + * If any of the loop control fields are non-\c NULL, this field must be + * one of \c ir_binop_less, \c ir_binop_greater, \c ir_binop_lequal, + * \c ir_binop_gequal, \c ir_binop_equal, or \c ir_binop_nequal. + */ + int cmp; + /*@}*/ +}; + + +class ir_assignment : public ir_instruction { +public: + ir_assignment(ir_rvalue *lhs, ir_rvalue *rhs, ir_rvalue *condition = NULL); + + /** + * Construct an assignment with an explicit write mask + * + * \note + * Since a write mask is supplied, the LHS must already be a bare + * \c ir_dereference. The cannot be any swizzles in the LHS. + */ + ir_assignment(ir_dereference *lhs, ir_rvalue *rhs, ir_rvalue *condition, + unsigned write_mask); + + virtual ir_assignment *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual ir_constant *constant_expression_value(); + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + virtual ir_assignment * as_assignment() + { + return this; + } + + /** + * Get a whole variable written by an assignment + * + * If the LHS of the assignment writes a whole variable, the variable is + * returned. Otherwise \c NULL is returned. Examples of whole-variable + * assignment are: + * + * - Assigning to a scalar + * - Assigning to all components of a vector + * - Whole array (or matrix) assignment + * - Whole structure assignment + */ + ir_variable *whole_variable_written(); + + /** + * Set the LHS of an assignment + */ + void set_lhs(ir_rvalue *lhs); + + /** + * Left-hand side of the assignment. + * + * This should be treated as read only. If you need to set the LHS of an + * assignment, use \c ir_assignment::set_lhs. + */ + ir_dereference *lhs; + + /** + * Value being assigned + */ + ir_rvalue *rhs; + + /** + * Optional condition for the assignment. + */ + ir_rvalue *condition; + + + /** + * Component mask written + * + * For non-vector types in the LHS, this field will be zero. For vector + * types, a bit will be set for each component that is written. Note that + * for \c vec2 and \c vec3 types only the lower bits will ever be set. + * + * A partially-set write mask means that each enabled channel gets + * the value from a consecutive channel of the rhs. For example, + * to write just .xyw of gl_FrontColor with color: + * + * (assign (constant bool (1)) (xyw) + * (var_ref gl_FragColor) + * (swiz xyw (var_ref color))) + */ + unsigned write_mask:4; +}; + +/* Update ir_expression::num_operands() and operator_strs when + * updating this list. + */ +enum ir_expression_operation { + ir_unop_bit_not, + ir_unop_logic_not, + ir_unop_neg, + ir_unop_abs, + ir_unop_sign, + ir_unop_rcp, + ir_unop_rsq, + ir_unop_sqrt, + ir_unop_exp, /**< Log base e on gentype */ + ir_unop_log, /**< Natural log on gentype */ + ir_unop_exp2, + ir_unop_log2, + ir_unop_f2i, /**< Float-to-integer conversion. */ + ir_unop_i2f, /**< Integer-to-float conversion. */ + ir_unop_f2b, /**< Float-to-boolean conversion */ + ir_unop_b2f, /**< Boolean-to-float conversion */ + ir_unop_i2b, /**< int-to-boolean conversion */ + ir_unop_b2i, /**< Boolean-to-int conversion */ + ir_unop_u2f, /**< Unsigned-to-float conversion. */ + ir_unop_i2u, /**< Integer-to-unsigned conversion. */ + ir_unop_u2i, /**< Unsigned-to-integer conversion. */ + ir_unop_any, + + /** + * \name Unary floating-point rounding operations. + */ + /*@{*/ + ir_unop_trunc, + ir_unop_ceil, + ir_unop_floor, + ir_unop_fract, + ir_unop_round_even, + /*@}*/ + + /** + * \name Trigonometric operations. + */ + /*@{*/ + ir_unop_sin, + ir_unop_cos, + ir_unop_sin_reduced, /**< Reduced range sin. [-pi, pi] */ + ir_unop_cos_reduced, /**< Reduced range cos. [-pi, pi] */ + /*@}*/ + + /** + * \name Partial derivatives. + */ + /*@{*/ + ir_unop_dFdx, + ir_unop_dFdy, + /*@}*/ + + ir_unop_noise, + + /** + * A sentinel marking the last of the unary operations. + */ + ir_last_unop = ir_unop_noise, + + ir_binop_add, + ir_binop_sub, + ir_binop_mul, + ir_binop_div, + + /** + * Takes one of two combinations of arguments: + * + * - mod(vecN, vecN) + * - mod(vecN, float) + * + * Does not take integer types. + */ + ir_binop_mod, + + /** + * \name Binary comparison operators which return a boolean vector. + * The type of both operands must be equal. + */ + /*@{*/ + ir_binop_less, + ir_binop_greater, + ir_binop_lequal, + ir_binop_gequal, + ir_binop_equal, + ir_binop_nequal, + /** + * Returns single boolean for whether all components of operands[0] + * equal the components of operands[1]. + */ + ir_binop_all_equal, + /** + * Returns single boolean for whether any component of operands[0] + * is not equal to the corresponding component of operands[1]. + */ + ir_binop_any_nequal, + /*@}*/ + + /** + * \name Bit-wise binary operations. + */ + /*@{*/ + ir_binop_lshift, + ir_binop_rshift, + ir_binop_bit_and, + ir_binop_bit_xor, + ir_binop_bit_or, + /*@}*/ + + ir_binop_logic_and, + ir_binop_logic_xor, + ir_binop_logic_or, + + ir_binop_dot, + ir_binop_min, + ir_binop_max, + + ir_binop_pow, + + /** + * A sentinel marking the last of the binary operations. + */ + ir_last_binop = ir_binop_pow, + + ir_quadop_vector, + + /** + * A sentinel marking the last of all operations. + */ + ir_last_opcode = ir_last_binop +}; + +class ir_expression : public ir_rvalue { +public: + /** + * Constructor for unary operation expressions + */ + ir_expression(int op, const struct glsl_type *type, ir_rvalue *); + ir_expression(int op, ir_rvalue *); + + /** + * Constructor for binary operation expressions + */ + ir_expression(int op, const struct glsl_type *type, + ir_rvalue *, ir_rvalue *); + ir_expression(int op, ir_rvalue *op0, ir_rvalue *op1); + + /** + * Constructor for quad operator expressions + */ + ir_expression(int op, const struct glsl_type *type, + ir_rvalue *, ir_rvalue *, ir_rvalue *, ir_rvalue *); + + virtual ir_expression *as_expression() + { + return this; + } + + virtual ir_expression *clone(void *mem_ctx, struct hash_table *ht) const; + + /** + * Attempt to constant-fold the expression + * + * If the expression cannot be constant folded, this method will return + * \c NULL. + */ + virtual ir_constant *constant_expression_value(); + + /** + * Determine the number of operands used by an expression + */ + static unsigned int get_num_operands(ir_expression_operation); + + /** + * Determine the number of operands used by an expression + */ + unsigned int get_num_operands() const + { + return (this->operation == ir_quadop_vector) + ? this->type->vector_elements : get_num_operands(operation); + } + + /** + * Return a string representing this expression's operator. + */ + const char *operator_string(); + + /** + * Return a string representing this expression's operator. + */ + static const char *operator_string(ir_expression_operation); + + + /** + * Do a reverse-lookup to translate the given string into an operator. + */ + static ir_expression_operation get_operator(const char *); + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + ir_expression_operation operation; + ir_rvalue *operands[4]; +}; + + +/** + * IR instruction representing a function call + */ +class ir_call : public ir_rvalue { +public: + ir_call(ir_function_signature *callee, exec_list *actual_parameters) + : callee(callee) + { + ir_type = ir_type_call; + assert(callee->return_type != NULL); + type = callee->return_type; + actual_parameters->move_nodes_to(& this->actual_parameters); + this->use_builtin = callee->is_builtin; + } + + virtual ir_call *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual ir_constant *constant_expression_value(); + + virtual ir_call *as_call() + { + return this; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + /** + * Get a generic ir_call object when an error occurs + * + * Any allocation will be performed with 'ctx' as ralloc owner. + */ + static ir_call *get_error_instruction(void *ctx); + + /** + * Get an iterator for the set of acutal parameters + */ + exec_list_iterator iterator() + { + return actual_parameters.iterator(); + } + + /** + * Get the name of the function being called. + */ + const char *callee_name() const + { + return callee->function_name(); + } + + /** + * Get the function signature bound to this function call + */ + ir_function_signature *get_callee() + { + return callee; + } + + /** + * Set the function call target + */ + void set_callee(ir_function_signature *sig); + + /** + * Generates an inline version of the function before @ir, + * returning the return value of the function. + */ + ir_rvalue *generate_inline(ir_instruction *ir); + + /* List of ir_rvalue of paramaters passed in this call. */ + exec_list actual_parameters; + + /** Should this call only bind to a built-in function? */ + bool use_builtin; + +private: + ir_call() + : callee(NULL) + { + this->ir_type = ir_type_call; + } + + ir_function_signature *callee; +}; + + +/** + * \name Jump-like IR instructions. + * + * These include \c break, \c continue, \c return, and \c discard. + */ +/*@{*/ +class ir_jump : public ir_instruction { +protected: + ir_jump() + { + ir_type = ir_type_unset; + } +}; + +class ir_return : public ir_jump { +public: + ir_return() + : value(NULL) + { + this->ir_type = ir_type_return; + } + + ir_return(ir_rvalue *value) + : value(value) + { + this->ir_type = ir_type_return; + } + + virtual ir_return *clone(void *mem_ctx, struct hash_table *) const; + + virtual ir_return *as_return() + { + return this; + } + + ir_rvalue *get_value() const + { + return value; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + ir_rvalue *value; +}; + + +/** + * Jump instructions used inside loops + * + * These include \c break and \c continue. The \c break within a loop is + * different from the \c break within a switch-statement. + * + * \sa ir_switch_jump + */ +class ir_loop_jump : public ir_jump { +public: + enum jump_mode { + jump_break, + jump_continue + }; + + ir_loop_jump(jump_mode mode) + { + this->ir_type = ir_type_loop_jump; + this->mode = mode; + this->loop = loop; + } + + virtual ir_loop_jump *clone(void *mem_ctx, struct hash_table *) const; + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + bool is_break() const + { + return mode == jump_break; + } + + bool is_continue() const + { + return mode == jump_continue; + } + + /** Mode selector for the jump instruction. */ + enum jump_mode mode; +private: + /** Loop containing this break instruction. */ + ir_loop *loop; +}; + +/** + * IR instruction representing discard statements. + */ +class ir_discard : public ir_jump { +public: + ir_discard() + { + this->ir_type = ir_type_discard; + this->condition = NULL; + } + + ir_discard(ir_rvalue *cond) + { + this->ir_type = ir_type_discard; + this->condition = cond; + } + + virtual ir_discard *clone(void *mem_ctx, struct hash_table *ht) const; + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + virtual ir_discard *as_discard() + { + return this; + } + + ir_rvalue *condition; +}; +/*@}*/ + + +/** + * Texture sampling opcodes used in ir_texture + */ +enum ir_texture_opcode { + ir_tex, /**< Regular texture look-up */ + ir_txb, /**< Texture look-up with LOD bias */ + ir_txl, /**< Texture look-up with explicit LOD */ + ir_txd, /**< Texture look-up with partial derivatvies */ + ir_txf, /**< Texel fetch with explicit LOD */ + ir_txs /**< Texture size */ +}; + + +/** + * IR instruction to sample a texture + * + * The specific form of the IR instruction depends on the \c mode value + * selected from \c ir_texture_opcodes. In the printed IR, these will + * appear as: + * + * Texel offset (0 or an expression) + * | Projection divisor + * | | Shadow comparitor + * | | | + * v v v + * (tex 0 1 ( )) + * (txb 0 1 ( ) ) + * (txl 0 1 ( ) ) + * (txd 0 1 ( ) (dPdx dPdy)) + * (txf 0 ) + * (txs ) + */ +class ir_texture : public ir_rvalue { +public: + ir_texture(enum ir_texture_opcode op) + : op(op), projector(NULL), shadow_comparitor(NULL), offset(NULL) + { + this->ir_type = ir_type_texture; + } + + virtual ir_texture *clone(void *mem_ctx, struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + /** + * Return a string representing the ir_texture_opcode. + */ + const char *opcode_string(); + + /** Set the sampler and type. */ + void set_sampler(ir_dereference *sampler, const glsl_type *type); + + /** + * Do a reverse-lookup to translate a string into an ir_texture_opcode. + */ + static ir_texture_opcode get_opcode(const char *); + + enum ir_texture_opcode op; + + /** Sampler to use for the texture access. */ + ir_dereference *sampler; + + /** Texture coordinate to sample */ + ir_rvalue *coordinate; + + /** + * Value used for projective divide. + * + * If there is no projective divide (the common case), this will be + * \c NULL. Optimization passes should check for this to point to a constant + * of 1.0 and replace that with \c NULL. + */ + ir_rvalue *projector; + + /** + * Coordinate used for comparison on shadow look-ups. + * + * If there is no shadow comparison, this will be \c NULL. For the + * \c ir_txf opcode, this *must* be \c NULL. + */ + ir_rvalue *shadow_comparitor; + + /** Texel offset. */ + ir_rvalue *offset; + + union { + ir_rvalue *lod; /**< Floating point LOD */ + ir_rvalue *bias; /**< Floating point LOD bias */ + struct { + ir_rvalue *dPdx; /**< Partial derivative of coordinate wrt X */ + ir_rvalue *dPdy; /**< Partial derivative of coordinate wrt Y */ + } grad; + } lod_info; +}; + + +struct ir_swizzle_mask { + unsigned x:2; + unsigned y:2; + unsigned z:2; + unsigned w:2; + + /** + * Number of components in the swizzle. + */ + unsigned num_components:3; + + /** + * Does the swizzle contain duplicate components? + * + * L-value swizzles cannot contain duplicate components. + */ + unsigned has_duplicates:1; +}; + + +class ir_swizzle : public ir_rvalue { +public: + ir_swizzle(ir_rvalue *, unsigned x, unsigned y, unsigned z, unsigned w, + unsigned count); + + ir_swizzle(ir_rvalue *val, const unsigned *components, unsigned count); + + ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask); + + virtual ir_swizzle *clone(void *mem_ctx, struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + virtual ir_swizzle *as_swizzle() + { + return this; + } + + /** + * Construct an ir_swizzle from the textual representation. Can fail. + */ + static ir_swizzle *create(ir_rvalue *, const char *, unsigned vector_length); + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + bool is_lvalue() const + { + return val->is_lvalue() && !mask.has_duplicates; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const; + + ir_rvalue *val; + ir_swizzle_mask mask; + +private: + /** + * Initialize the mask component of a swizzle + * + * This is used by the \c ir_swizzle constructors. + */ + void init_mask(const unsigned *components, unsigned count); +}; + + +class ir_dereference : public ir_rvalue { +public: + virtual ir_dereference *clone(void *mem_ctx, struct hash_table *) const = 0; + + virtual ir_dereference *as_dereference() + { + return this; + } + + bool is_lvalue() const; + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const = 0; +}; + + +class ir_dereference_variable : public ir_dereference { +public: + ir_dereference_variable(ir_variable *var); + + virtual ir_dereference_variable *clone(void *mem_ctx, + struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + virtual ir_dereference_variable *as_dereference_variable() + { + return this; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const + { + return this->var; + } + + virtual ir_variable *whole_variable_referenced() + { + /* ir_dereference_variable objects always dereference the entire + * variable. However, if this dereference is dereferenced by anything + * else, the complete deferefernce chain is not a whole-variable + * dereference. This method should only be called on the top most + * ir_rvalue in a dereference chain. + */ + return this->var; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + /** + * Object being dereferenced. + */ + ir_variable *var; +}; + + +class ir_dereference_array : public ir_dereference { +public: + ir_dereference_array(ir_rvalue *value, ir_rvalue *array_index); + + ir_dereference_array(ir_variable *var, ir_rvalue *array_index); + + virtual ir_dereference_array *clone(void *mem_ctx, + struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + virtual ir_dereference_array *as_dereference_array() + { + return this; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const + { + return this->array->variable_referenced(); + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + ir_rvalue *array; + ir_rvalue *array_index; + +private: + void set_array(ir_rvalue *value); +}; + + +class ir_dereference_record : public ir_dereference { +public: + ir_dereference_record(ir_rvalue *value, const char *field); + + ir_dereference_record(ir_variable *var, const char *field); + + virtual ir_dereference_record *clone(void *mem_ctx, + struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() const + { + return this->record->variable_referenced(); + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + ir_rvalue *record; + const char *field; +}; + + +/** + * Data stored in an ir_constant + */ +union ir_constant_data { + unsigned u[16]; + int i[16]; + float f[16]; + bool b[16]; +}; + + +class ir_constant : public ir_rvalue { +public: + ir_constant(const struct glsl_type *type, const ir_constant_data *data); + ir_constant(bool b); + ir_constant(unsigned int u); + ir_constant(int i); + ir_constant(float f); + + /** + * Construct an ir_constant from a list of ir_constant values + */ + ir_constant(const struct glsl_type *type, exec_list *values); + + /** + * Construct an ir_constant from a scalar component of another ir_constant + * + * The new \c ir_constant inherits the type of the component from the + * source constant. + * + * \note + * In the case of a matrix constant, the new constant is a scalar, \b not + * a vector. + */ + ir_constant(const ir_constant *c, unsigned i); + + /** + * Return a new ir_constant of the specified type containing all zeros. + */ + static ir_constant *zero(void *mem_ctx, const glsl_type *type); + + virtual ir_constant *clone(void *mem_ctx, struct hash_table *) const; + + virtual ir_constant *constant_expression_value(); + + virtual ir_constant *as_constant() + { + return this; + } + + virtual void accept(ir_visitor *v) + { + v->visit(this); + } + + virtual ir_visitor_status accept(ir_hierarchical_visitor *); + + /** + * Get a particular component of a constant as a specific type + * + * This is useful, for example, to get a value from an integer constant + * as a float or bool. This appears frequently when constructors are + * called with all constant parameters. + */ + /*@{*/ + bool get_bool_component(unsigned i) const; + float get_float_component(unsigned i) const; + int get_int_component(unsigned i) const; + unsigned get_uint_component(unsigned i) const; + /*@}*/ + + ir_constant *get_array_element(unsigned i) const; + + ir_constant *get_record_field(const char *name); + + /** + * Determine whether a constant has the same value as another constant + * + * \sa ir_constant::is_zero, ir_constant::is_one, + * ir_constant::is_negative_one + */ + bool has_value(const ir_constant *) const; + + virtual bool is_zero() const; + virtual bool is_one() const; + virtual bool is_negative_one() const; + + /** + * Value of the constant. + * + * The field used to back the values supplied by the constant is determined + * by the type associated with the \c ir_instruction. Constants may be + * scalars, vectors, or matrices. + */ + union ir_constant_data value; + + /* Array elements */ + ir_constant **array_elements; + + /* Structure fields */ + exec_list components; + +private: + /** + * Parameterless constructor only used by the clone method + */ + ir_constant(void); +}; + +/*@}*/ + +/** + * Apply a visitor to each IR node in a list + */ +void +visit_exec_list(exec_list *list, ir_visitor *visitor); + +/** + * Validate invariants on each IR node in a list + */ +void validate_ir_tree(exec_list *instructions); + +struct _mesa_glsl_parse_state; +struct gl_shader_program; + +/** + * Detect whether an unlinked shader contains static recursion + * + * If the list of instructions is determined to contain static recursion, + * \c _mesa_glsl_error will be called to emit error messages for each function + * that is in the recursion cycle. + */ +void +detect_recursion_unlinked(struct _mesa_glsl_parse_state *state, + exec_list *instructions); + +/** + * Detect whether a linked shader contains static recursion + * + * If the list of instructions is determined to contain static recursion, + * \c link_error_printf will be called to emit error messages for each function + * that is in the recursion cycle. In addition, + * \c gl_shader_program::LinkStatus will be set to false. + */ +void +detect_recursion_linked(struct gl_shader_program *prog, + exec_list *instructions); + +/** + * Make a clone of each IR instruction in a list + * + * \param in List of IR instructions that are to be cloned + * \param out List to hold the cloned instructions + */ +void +clone_ir_list(void *mem_ctx, exec_list *out, const exec_list *in); + +extern void +_mesa_glsl_initialize_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + +extern void +_mesa_glsl_initialize_functions(_mesa_glsl_parse_state *state); + +extern void +_mesa_glsl_release_functions(void); + +extern void +reparent_ir(exec_list *list, void *mem_ctx); + +struct glsl_symbol_table; + +extern void +import_prototypes(const exec_list *source, exec_list *dest, + struct glsl_symbol_table *symbols, void *mem_ctx); + +extern bool +ir_has_call(ir_instruction *ir); + +extern void +do_set_program_inouts(exec_list *instructions, struct gl_program *prog); + +extern char * +prototype_string(const glsl_type *return_type, const char *name, + exec_list *parameters); + +#endif /* IR_H */ diff --git a/mesalib/src/glsl/ir_clone.cpp b/mesalib/src/glsl/ir_clone.cpp index 8260b1339..f0757365d 100644 --- a/mesalib/src/glsl/ir_clone.cpp +++ b/mesalib/src/glsl/ir_clone.cpp @@ -1,434 +1,434 @@ -/* - * 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 -#include "main/compiler.h" -#include "ir.h" -#include "glsl_types.h" -extern "C" { -#include "program/hash_table.h" -} - -/** - * Duplicate an IR variable - * - * \note - * This will probably be made \c virtual and moved to the base class - * eventually. - */ -ir_variable * -ir_variable::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_variable *var = new(mem_ctx) ir_variable(this->type, this->name, - (ir_variable_mode) this->mode); - - var->max_array_access = this->max_array_access; - var->read_only = this->read_only; - var->centroid = this->centroid; - var->invariant = this->invariant; - var->interpolation = this->interpolation; - var->array_lvalue = this->array_lvalue; - var->location = this->location; - var->warn_extension = this->warn_extension; - var->origin_upper_left = this->origin_upper_left; - var->pixel_center_integer = this->pixel_center_integer; - var->explicit_location = this->explicit_location; - - var->num_state_slots = this->num_state_slots; - if (this->state_slots) { - /* FINISHME: This really wants to use something like talloc_reference, but - * FINISHME: ralloc doesn't have any similar function. - */ - var->state_slots = ralloc_array(var, ir_state_slot, - this->num_state_slots); - memcpy(var->state_slots, this->state_slots, - sizeof(this->state_slots[0]) * var->num_state_slots); - } - - if (this->explicit_location) - var->location = this->location; - - if (this->constant_value) - var->constant_value = this->constant_value->clone(mem_ctx, ht); - - if (ht) { - hash_table_insert(ht, var, (void *)const_cast(this)); - } - - return var; -} - -ir_swizzle * -ir_swizzle::clone(void *mem_ctx, struct hash_table *ht) const -{ - return new(mem_ctx) ir_swizzle(this->val->clone(mem_ctx, ht), this->mask); -} - -ir_return * -ir_return::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_rvalue *new_value = NULL; - - if (this->value) - new_value = this->value->clone(mem_ctx, ht); - - return new(mem_ctx) ir_return(new_value); -} - -ir_discard * -ir_discard::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_rvalue *new_condition = NULL; - - if (this->condition != NULL) - new_condition = this->condition->clone(mem_ctx, ht); - - return new(mem_ctx) ir_discard(new_condition); -} - -ir_loop_jump * -ir_loop_jump::clone(void *mem_ctx, struct hash_table *ht) const -{ - (void)ht; - - return new(mem_ctx) ir_loop_jump(this->mode); -} - -ir_if * -ir_if::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_if *new_if = new(mem_ctx) ir_if(this->condition->clone(mem_ctx, ht)); - - foreach_iter(exec_list_iterator, iter, this->then_instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - new_if->then_instructions.push_tail(ir->clone(mem_ctx, ht)); - } - - foreach_iter(exec_list_iterator, iter, this->else_instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - new_if->else_instructions.push_tail(ir->clone(mem_ctx, ht)); - } - - return new_if; -} - -ir_loop * -ir_loop::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_loop *new_loop = new(mem_ctx) ir_loop(); - - if (this->from) - new_loop->from = this->from->clone(mem_ctx, ht); - if (this->to) - new_loop->to = this->to->clone(mem_ctx, ht); - if (this->increment) - new_loop->increment = this->increment->clone(mem_ctx, ht); - new_loop->counter = counter; - - foreach_iter(exec_list_iterator, iter, this->body_instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - new_loop->body_instructions.push_tail(ir->clone(mem_ctx, ht)); - } - - new_loop->cmp = this->cmp; - return new_loop; -} - -ir_call * -ir_call::clone(void *mem_ctx, struct hash_table *ht) const -{ - if (this->type == glsl_type::error_type) - return ir_call::get_error_instruction(mem_ctx); - - exec_list new_parameters; - - foreach_iter(exec_list_iterator, iter, this->actual_parameters) { - ir_instruction *ir = (ir_instruction *)iter.get(); - new_parameters.push_tail(ir->clone(mem_ctx, ht)); - } - - return new(mem_ctx) ir_call(this->callee, &new_parameters); -} - -ir_expression * -ir_expression::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_rvalue *op[Elements(this->operands)] = { NULL, }; - unsigned int i; - - for (i = 0; i < get_num_operands(); i++) { - op[i] = this->operands[i]->clone(mem_ctx, ht); - } - - return new(mem_ctx) ir_expression(this->operation, this->type, - op[0], op[1], op[2], op[3]); -} - -ir_dereference_variable * -ir_dereference_variable::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_variable *new_var; - - if (ht) { - new_var = (ir_variable *)hash_table_find(ht, this->var); - if (!new_var) - new_var = this->var; - } else { - new_var = this->var; - } - - return new(mem_ctx) ir_dereference_variable(new_var); -} - -ir_dereference_array * -ir_dereference_array::clone(void *mem_ctx, struct hash_table *ht) const -{ - return new(mem_ctx) ir_dereference_array(this->array->clone(mem_ctx, ht), - this->array_index->clone(mem_ctx, - ht)); -} - -ir_dereference_record * -ir_dereference_record::clone(void *mem_ctx, struct hash_table *ht) const -{ - return new(mem_ctx) ir_dereference_record(this->record->clone(mem_ctx, ht), - this->field); -} - -ir_texture * -ir_texture::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_texture *new_tex = new(mem_ctx) ir_texture(this->op); - new_tex->type = this->type; - - new_tex->sampler = this->sampler->clone(mem_ctx, ht); - if (this->coordinate) - new_tex->coordinate = this->coordinate->clone(mem_ctx, ht); - if (this->projector) - new_tex->projector = this->projector->clone(mem_ctx, ht); - if (this->shadow_comparitor) { - new_tex->shadow_comparitor = this->shadow_comparitor->clone(mem_ctx, ht); - } - - if (this->offset != NULL) - new_tex->offset = this->offset->clone(mem_ctx, ht); - - switch (this->op) { - case ir_tex: - break; - case ir_txb: - new_tex->lod_info.bias = this->lod_info.bias->clone(mem_ctx, ht); - break; - case ir_txl: - case ir_txf: - case ir_txs: - new_tex->lod_info.lod = this->lod_info.lod->clone(mem_ctx, ht); - break; - case ir_txd: - new_tex->lod_info.grad.dPdx = this->lod_info.grad.dPdx->clone(mem_ctx, ht); - new_tex->lod_info.grad.dPdy = this->lod_info.grad.dPdy->clone(mem_ctx, ht); - break; - } - - return new_tex; -} - -ir_assignment * -ir_assignment::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_rvalue *new_condition = NULL; - - if (this->condition) - new_condition = this->condition->clone(mem_ctx, ht); - - return new(mem_ctx) ir_assignment(this->lhs->clone(mem_ctx, ht), - this->rhs->clone(mem_ctx, ht), - new_condition, - this->write_mask); -} - -ir_function * -ir_function::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_function *copy = new(mem_ctx) ir_function(this->name); - - foreach_list_const(node, &this->signatures) { - const ir_function_signature *const sig = - (const ir_function_signature *const) node; - - ir_function_signature *sig_copy = sig->clone(mem_ctx, ht); - copy->add_signature(sig_copy); - - if (ht != NULL) - hash_table_insert(ht, sig_copy, - (void *)const_cast(sig)); - } - - return copy; -} - -ir_function_signature * -ir_function_signature::clone(void *mem_ctx, struct hash_table *ht) const -{ - ir_function_signature *copy = this->clone_prototype(mem_ctx, ht); - - copy->is_defined = this->is_defined; - - /* Clone the instruction list. - */ - foreach_list_const(node, &this->body) { - const ir_instruction *const inst = (const ir_instruction *) node; - - ir_instruction *const inst_copy = inst->clone(mem_ctx, ht); - copy->body.push_tail(inst_copy); - } - - return copy; -} - -ir_function_signature * -ir_function_signature::clone_prototype(void *mem_ctx, struct hash_table *ht) const -{ - ir_function_signature *copy = - new(mem_ctx) ir_function_signature(this->return_type); - - copy->is_defined = false; - copy->is_builtin = this->is_builtin; - - /* Clone the parameter list, but NOT the body. - */ - foreach_list_const(node, &this->parameters) { - const ir_variable *const param = (const ir_variable *) node; - - assert(const_cast(param)->as_variable() != NULL); - - ir_variable *const param_copy = param->clone(mem_ctx, ht); - copy->parameters.push_tail(param_copy); - } - - return copy; -} - -ir_constant * -ir_constant::clone(void *mem_ctx, struct hash_table *ht) const -{ - (void)ht; - - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_BOOL: - return new(mem_ctx) ir_constant(this->type, &this->value); - - case GLSL_TYPE_STRUCT: { - ir_constant *c = new(mem_ctx) ir_constant; - - c->type = this->type; - for (exec_node *node = this->components.head - ; !node->is_tail_sentinel() - ; node = node->next) { - ir_constant *const orig = (ir_constant *) node; - - c->components.push_tail(orig->clone(mem_ctx, NULL)); - } - - return c; - } - - case GLSL_TYPE_ARRAY: { - ir_constant *c = new(mem_ctx) ir_constant; - - c->type = this->type; - c->array_elements = ralloc_array(c, ir_constant *, this->type->length); - for (unsigned i = 0; i < this->type->length; i++) { - c->array_elements[i] = this->array_elements[i]->clone(mem_ctx, NULL); - } - return c; - } - - default: - assert(!"Should not get here."); - return NULL; - } -} - - -class fixup_ir_call_visitor : public ir_hierarchical_visitor { -public: - fixup_ir_call_visitor(struct hash_table *ht) - { - this->ht = ht; - } - - virtual ir_visitor_status visit_enter(ir_call *ir) - { - /* Try to find the function signature referenced by the ir_call in the - * table. If it is found, replace it with the value from the table. - */ - ir_function_signature *sig = - (ir_function_signature *) hash_table_find(this->ht, ir->get_callee()); - if (sig != NULL) - ir->set_callee(sig); - - /* Since this may be used before function call parameters are flattened, - * the children also need to be processed. - */ - return visit_continue; - } - -private: - struct hash_table *ht; -}; - - -static void -fixup_function_calls(struct hash_table *ht, exec_list *instructions) -{ - fixup_ir_call_visitor v(ht); - v.run(instructions); -} - - -void -clone_ir_list(void *mem_ctx, exec_list *out, const exec_list *in) -{ - struct hash_table *ht = - hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); - - foreach_list_const(node, in) { - const ir_instruction *const original = (ir_instruction *) node; - ir_instruction *copy = original->clone(mem_ctx, ht); - - out->push_tail(copy); - } - - /* Make a pass over the cloned tree to fix up ir_call nodes to point to the - * cloned ir_function_signature nodes. This cannot be done automatically - * during cloning because the ir_call might be a forward reference (i.e., - * the function signature that it references may not have been cloned yet). - */ - fixup_function_calls(ht, out); - - hash_table_dtor(ht); -} +/* + * 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 +#include "main/compiler.h" +#include "ir.h" +#include "glsl_types.h" +extern "C" { +#include "program/hash_table.h" +} + +/** + * Duplicate an IR variable + * + * \note + * This will probably be made \c virtual and moved to the base class + * eventually. + */ +ir_variable * +ir_variable::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_variable *var = new(mem_ctx) ir_variable(this->type, this->name, + (ir_variable_mode) this->mode); + + var->max_array_access = this->max_array_access; + var->read_only = this->read_only; + var->centroid = this->centroid; + var->invariant = this->invariant; + var->interpolation = this->interpolation; + var->array_lvalue = this->array_lvalue; + var->location = this->location; + var->warn_extension = this->warn_extension; + var->origin_upper_left = this->origin_upper_left; + var->pixel_center_integer = this->pixel_center_integer; + var->explicit_location = this->explicit_location; + + var->num_state_slots = this->num_state_slots; + if (this->state_slots) { + /* FINISHME: This really wants to use something like talloc_reference, but + * FINISHME: ralloc doesn't have any similar function. + */ + var->state_slots = ralloc_array(var, ir_state_slot, + this->num_state_slots); + memcpy(var->state_slots, this->state_slots, + sizeof(this->state_slots[0]) * var->num_state_slots); + } + + if (this->explicit_location) + var->location = this->location; + + if (this->constant_value) + var->constant_value = this->constant_value->clone(mem_ctx, ht); + + if (ht) { + hash_table_insert(ht, var, (void *)const_cast(this)); + } + + return var; +} + +ir_swizzle * +ir_swizzle::clone(void *mem_ctx, struct hash_table *ht) const +{ + return new(mem_ctx) ir_swizzle(this->val->clone(mem_ctx, ht), this->mask); +} + +ir_return * +ir_return::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_rvalue *new_value = NULL; + + if (this->value) + new_value = this->value->clone(mem_ctx, ht); + + return new(mem_ctx) ir_return(new_value); +} + +ir_discard * +ir_discard::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_rvalue *new_condition = NULL; + + if (this->condition != NULL) + new_condition = this->condition->clone(mem_ctx, ht); + + return new(mem_ctx) ir_discard(new_condition); +} + +ir_loop_jump * +ir_loop_jump::clone(void *mem_ctx, struct hash_table *ht) const +{ + (void)ht; + + return new(mem_ctx) ir_loop_jump(this->mode); +} + +ir_if * +ir_if::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_if *new_if = new(mem_ctx) ir_if(this->condition->clone(mem_ctx, ht)); + + foreach_iter(exec_list_iterator, iter, this->then_instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + new_if->then_instructions.push_tail(ir->clone(mem_ctx, ht)); + } + + foreach_iter(exec_list_iterator, iter, this->else_instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + new_if->else_instructions.push_tail(ir->clone(mem_ctx, ht)); + } + + return new_if; +} + +ir_loop * +ir_loop::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_loop *new_loop = new(mem_ctx) ir_loop(); + + if (this->from) + new_loop->from = this->from->clone(mem_ctx, ht); + if (this->to) + new_loop->to = this->to->clone(mem_ctx, ht); + if (this->increment) + new_loop->increment = this->increment->clone(mem_ctx, ht); + new_loop->counter = counter; + + foreach_iter(exec_list_iterator, iter, this->body_instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + new_loop->body_instructions.push_tail(ir->clone(mem_ctx, ht)); + } + + new_loop->cmp = this->cmp; + return new_loop; +} + +ir_call * +ir_call::clone(void *mem_ctx, struct hash_table *ht) const +{ + if (this->type == glsl_type::error_type) + return ir_call::get_error_instruction(mem_ctx); + + exec_list new_parameters; + + foreach_iter(exec_list_iterator, iter, this->actual_parameters) { + ir_instruction *ir = (ir_instruction *)iter.get(); + new_parameters.push_tail(ir->clone(mem_ctx, ht)); + } + + return new(mem_ctx) ir_call(this->callee, &new_parameters); +} + +ir_expression * +ir_expression::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_rvalue *op[Elements(this->operands)] = { NULL, }; + unsigned int i; + + for (i = 0; i < get_num_operands(); i++) { + op[i] = this->operands[i]->clone(mem_ctx, ht); + } + + return new(mem_ctx) ir_expression(this->operation, this->type, + op[0], op[1], op[2], op[3]); +} + +ir_dereference_variable * +ir_dereference_variable::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_variable *new_var; + + if (ht) { + new_var = (ir_variable *)hash_table_find(ht, this->var); + if (!new_var) + new_var = this->var; + } else { + new_var = this->var; + } + + return new(mem_ctx) ir_dereference_variable(new_var); +} + +ir_dereference_array * +ir_dereference_array::clone(void *mem_ctx, struct hash_table *ht) const +{ + return new(mem_ctx) ir_dereference_array(this->array->clone(mem_ctx, ht), + this->array_index->clone(mem_ctx, + ht)); +} + +ir_dereference_record * +ir_dereference_record::clone(void *mem_ctx, struct hash_table *ht) const +{ + return new(mem_ctx) ir_dereference_record(this->record->clone(mem_ctx, ht), + this->field); +} + +ir_texture * +ir_texture::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_texture *new_tex = new(mem_ctx) ir_texture(this->op); + new_tex->type = this->type; + + new_tex->sampler = this->sampler->clone(mem_ctx, ht); + if (this->coordinate) + new_tex->coordinate = this->coordinate->clone(mem_ctx, ht); + if (this->projector) + new_tex->projector = this->projector->clone(mem_ctx, ht); + if (this->shadow_comparitor) { + new_tex->shadow_comparitor = this->shadow_comparitor->clone(mem_ctx, ht); + } + + if (this->offset != NULL) + new_tex->offset = this->offset->clone(mem_ctx, ht); + + switch (this->op) { + case ir_tex: + break; + case ir_txb: + new_tex->lod_info.bias = this->lod_info.bias->clone(mem_ctx, ht); + break; + case ir_txl: + case ir_txf: + case ir_txs: + new_tex->lod_info.lod = this->lod_info.lod->clone(mem_ctx, ht); + break; + case ir_txd: + new_tex->lod_info.grad.dPdx = this->lod_info.grad.dPdx->clone(mem_ctx, ht); + new_tex->lod_info.grad.dPdy = this->lod_info.grad.dPdy->clone(mem_ctx, ht); + break; + } + + return new_tex; +} + +ir_assignment * +ir_assignment::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_rvalue *new_condition = NULL; + + if (this->condition) + new_condition = this->condition->clone(mem_ctx, ht); + + return new(mem_ctx) ir_assignment(this->lhs->clone(mem_ctx, ht), + this->rhs->clone(mem_ctx, ht), + new_condition, + this->write_mask); +} + +ir_function * +ir_function::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_function *copy = new(mem_ctx) ir_function(this->name); + + foreach_list_const(node, &this->signatures) { + const ir_function_signature *const sig = + (const ir_function_signature *const) node; + + ir_function_signature *sig_copy = sig->clone(mem_ctx, ht); + copy->add_signature(sig_copy); + + if (ht != NULL) + hash_table_insert(ht, sig_copy, + (void *)const_cast(sig)); + } + + return copy; +} + +ir_function_signature * +ir_function_signature::clone(void *mem_ctx, struct hash_table *ht) const +{ + ir_function_signature *copy = this->clone_prototype(mem_ctx, ht); + + copy->is_defined = this->is_defined; + + /* Clone the instruction list. + */ + foreach_list_const(node, &this->body) { + const ir_instruction *const inst = (const ir_instruction *) node; + + ir_instruction *const inst_copy = inst->clone(mem_ctx, ht); + copy->body.push_tail(inst_copy); + } + + return copy; +} + +ir_function_signature * +ir_function_signature::clone_prototype(void *mem_ctx, struct hash_table *ht) const +{ + ir_function_signature *copy = + new(mem_ctx) ir_function_signature(this->return_type); + + copy->is_defined = false; + copy->is_builtin = this->is_builtin; + + /* Clone the parameter list, but NOT the body. + */ + foreach_list_const(node, &this->parameters) { + const ir_variable *const param = (const ir_variable *) node; + + assert(const_cast(param)->as_variable() != NULL); + + ir_variable *const param_copy = param->clone(mem_ctx, ht); + copy->parameters.push_tail(param_copy); + } + + return copy; +} + +ir_constant * +ir_constant::clone(void *mem_ctx, struct hash_table *ht) const +{ + (void)ht; + + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + case GLSL_TYPE_FLOAT: + case GLSL_TYPE_BOOL: + return new(mem_ctx) ir_constant(this->type, &this->value); + + case GLSL_TYPE_STRUCT: { + ir_constant *c = new(mem_ctx) ir_constant; + + c->type = this->type; + for (exec_node *node = this->components.head + ; !node->is_tail_sentinel() + ; node = node->next) { + ir_constant *const orig = (ir_constant *) node; + + c->components.push_tail(orig->clone(mem_ctx, NULL)); + } + + return c; + } + + case GLSL_TYPE_ARRAY: { + ir_constant *c = new(mem_ctx) ir_constant; + + c->type = this->type; + c->array_elements = ralloc_array(c, ir_constant *, this->type->length); + for (unsigned i = 0; i < this->type->length; i++) { + c->array_elements[i] = this->array_elements[i]->clone(mem_ctx, NULL); + } + return c; + } + + default: + assert(!"Should not get here."); + return NULL; + } +} + + +class fixup_ir_call_visitor : public ir_hierarchical_visitor { +public: + fixup_ir_call_visitor(struct hash_table *ht) + { + this->ht = ht; + } + + virtual ir_visitor_status visit_enter(ir_call *ir) + { + /* Try to find the function signature referenced by the ir_call in the + * table. If it is found, replace it with the value from the table. + */ + ir_function_signature *sig = + (ir_function_signature *) hash_table_find(this->ht, ir->get_callee()); + if (sig != NULL) + ir->set_callee(sig); + + /* Since this may be used before function call parameters are flattened, + * the children also need to be processed. + */ + return visit_continue; + } + +private: + struct hash_table *ht; +}; + + +static void +fixup_function_calls(struct hash_table *ht, exec_list *instructions) +{ + fixup_ir_call_visitor v(ht); + v.run(instructions); +} + + +void +clone_ir_list(void *mem_ctx, exec_list *out, const exec_list *in) +{ + struct hash_table *ht = + hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); + + foreach_list_const(node, in) { + const ir_instruction *const original = (ir_instruction *) node; + ir_instruction *copy = original->clone(mem_ctx, ht); + + out->push_tail(copy); + } + + /* Make a pass over the cloned tree to fix up ir_call nodes to point to the + * cloned ir_function_signature nodes. This cannot be done automatically + * during cloning because the ir_call might be a forward reference (i.e., + * the function signature that it references may not have been cloned yet). + */ + fixup_function_calls(ht, out); + + hash_table_dtor(ht); +} diff --git a/mesalib/src/glsl/ir_constant_expression.cpp b/mesalib/src/glsl/ir_constant_expression.cpp index 7988b566d..f0299a2c4 100644 --- a/mesalib/src/glsl/ir_constant_expression.cpp +++ b/mesalib/src/glsl/ir_constant_expression.cpp @@ -1,1383 +1,1383 @@ -/* - * 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. - */ - -/** - * \file ir_constant_expression.cpp - * Evaluate and process constant valued expressions - * - * In GLSL, constant valued expressions are used in several places. These - * must be processed and evaluated very early in the compilation process. - * - * * Sizes of arrays - * * Initializers for uniforms - * * Initializers for \c const variables - */ - -#include -#include "main/core.h" /* for MAX2, MIN2, CLAMP */ -#include "ir.h" -#include "ir_visitor.h" -#include "glsl_types.h" - -static float -dot(ir_constant *op0, ir_constant *op1) -{ - assert(op0->type->is_float() && op1->type->is_float()); - - float result = 0; - for (unsigned c = 0; c < op0->type->components(); c++) - result += op0->value.f[c] * op1->value.f[c]; - - return result; -} - -ir_constant * -ir_expression::constant_expression_value() -{ - if (this->type->is_error()) - return NULL; - - ir_constant *op[Elements(this->operands)] = { NULL, }; - ir_constant_data data; - - memset(&data, 0, sizeof(data)); - - for (unsigned operand = 0; operand < this->get_num_operands(); operand++) { - op[operand] = this->operands[operand]->constant_expression_value(); - if (!op[operand]) - return NULL; - } - - if (op[1] != NULL) - assert(op[0]->type->base_type == op[1]->type->base_type); - - bool op0_scalar = op[0]->type->is_scalar(); - bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar(); - - /* When iterating over a vector or matrix's components, we want to increase - * the loop counter. However, for scalars, we want to stay at 0. - */ - unsigned c0_inc = op0_scalar ? 0 : 1; - unsigned c1_inc = op1_scalar ? 0 : 1; - unsigned components; - if (op1_scalar || !op[1]) { - components = op[0]->type->components(); - } else { - components = op[1]->type->components(); - } - - void *ctx = ralloc_parent(this); - - /* Handle array operations here, rather than below. */ - if (op[0]->type->is_array()) { - assert(op[1] != NULL && op[1]->type->is_array()); - switch (this->operation) { - case ir_binop_all_equal: - return new(ctx) ir_constant(op[0]->has_value(op[1])); - case ir_binop_any_nequal: - return new(ctx) ir_constant(!op[0]->has_value(op[1])); - default: - break; - } - return NULL; - } - - switch (this->operation) { - case ir_unop_bit_not: - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - for (unsigned c = 0; c < components; c++) - data.i[c] = ~ op[0]->value.i[c]; - break; - case GLSL_TYPE_UINT: - for (unsigned c = 0; c < components; c++) - data.u[c] = ~ op[0]->value.u[c]; - break; - default: - assert(0); - } - break; - - case ir_unop_logic_not: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = !op[0]->value.b[c]; - break; - - case ir_unop_f2i: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.i[c] = (int) op[0]->value.f[c]; - } - break; - case ir_unop_i2f: - assert(op[0]->type->base_type == GLSL_TYPE_INT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = (float) op[0]->value.i[c]; - } - break; - case ir_unop_u2f: - assert(op[0]->type->base_type == GLSL_TYPE_UINT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = (float) op[0]->value.u[c]; - } - break; - case ir_unop_b2f: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F; - } - break; - case ir_unop_f2b: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.b[c] = op[0]->value.f[c] != 0.0F ? true : false; - } - break; - case ir_unop_b2i: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.u[c] = op[0]->value.b[c] ? 1 : 0; - } - break; - case ir_unop_i2b: - assert(op[0]->type->is_integer()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.b[c] = op[0]->value.u[c] ? true : false; - } - break; - case ir_unop_u2i: - assert(op[0]->type->base_type == GLSL_TYPE_UINT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.i[c] = op[0]->value.u[c]; - } - break; - case ir_unop_i2u: - assert(op[0]->type->base_type == GLSL_TYPE_INT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.u[c] = op[0]->value.i[c]; - } - break; - case ir_unop_any: - assert(op[0]->type->is_boolean()); - data.b[0] = false; - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (op[0]->value.b[c]) - data.b[0] = true; - } - break; - - case ir_unop_trunc: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = truncf(op[0]->value.f[c]); - } - break; - - case ir_unop_ceil: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = ceilf(op[0]->value.f[c]); - } - break; - - case ir_unop_floor: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = floorf(op[0]->value.f[c]); - } - break; - - case ir_unop_fract: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = 0; - break; - case GLSL_TYPE_INT: - data.i[c] = 0; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]); - break; - default: - assert(0); - } - } - break; - - case ir_unop_sin: - case ir_unop_sin_reduced: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = sinf(op[0]->value.f[c]); - } - break; - - case ir_unop_cos: - case ir_unop_cos_reduced: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = cosf(op[0]->value.f[c]); - } - break; - - case ir_unop_neg: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = -((int) op[0]->value.u[c]); - break; - case GLSL_TYPE_INT: - data.i[c] = -op[0]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = -op[0]->value.f[c]; - break; - default: - assert(0); - } - } - break; - - case ir_unop_abs: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c]; - if (data.i[c] < 0) - data.i[c] = -data.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = fabs(op[0]->value.f[c]); - break; - default: - assert(0); - } - } - break; - - case ir_unop_sign: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.i[c] > 0; - break; - case GLSL_TYPE_INT: - data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0)); - break; - default: - assert(0); - } - } - break; - - case ir_unop_rcp: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - if (op[0]->value.u[c] != 0.0) - data.u[c] = 1 / op[0]->value.u[c]; - break; - case GLSL_TYPE_INT: - if (op[0]->value.i[c] != 0.0) - data.i[c] = 1 / op[0]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - if (op[0]->value.f[c] != 0.0) - data.f[c] = 1.0F / op[0]->value.f[c]; - break; - default: - assert(0); - } - } - break; - - case ir_unop_rsq: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = 1.0F / sqrtf(op[0]->value.f[c]); - } - break; - - case ir_unop_sqrt: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = sqrtf(op[0]->value.f[c]); - } - break; - - case ir_unop_exp: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = expf(op[0]->value.f[c]); - } - break; - - case ir_unop_exp2: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = exp2f(op[0]->value.f[c]); - } - break; - - case ir_unop_log: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = logf(op[0]->value.f[c]); - } - break; - - case ir_unop_log2: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = log2f(op[0]->value.f[c]); - } - break; - - case ir_unop_dFdx: - case ir_unop_dFdy: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = 0.0; - } - break; - - case ir_binop_pow: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]); - } - break; - - case ir_binop_dot: - data.f[0] = dot(op[0], op[1]); - break; - - case ir_binop_min: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]); - break; - case GLSL_TYPE_INT: - data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - - break; - case ir_binop_max: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]); - break; - case GLSL_TYPE_INT: - data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - break; - - case ir_binop_add: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_sub: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_mul: - /* Check for equal types, or unequal types involving scalars */ - if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix()) - || op0_scalar || op1_scalar) { - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - } else { - assert(op[0]->type->is_matrix() || op[1]->type->is_matrix()); - - /* Multiply an N-by-M matrix with an M-by-P matrix. Since either - * matrix can be a GLSL vector, either N or P can be 1. - * - * For vec*mat, the vector is treated as a row vector. This - * means the vector is a 1-row x M-column matrix. - * - * For mat*vec, the vector is treated as a column vector. Since - * matrix_columns is 1 for vectors, this just works. - */ - const unsigned n = op[0]->type->is_vector() - ? 1 : op[0]->type->vector_elements; - const unsigned m = op[1]->type->vector_elements; - const unsigned p = op[1]->type->matrix_columns; - for (unsigned j = 0; j < p; j++) { - for (unsigned i = 0; i < n; i++) { - for (unsigned k = 0; k < m; k++) { - data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j]; - } - } - } - } - - break; - case ir_binop_div: - /* FINISHME: Emit warning when division-by-zero is detected. */ - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - if (op[1]->value.u[c1] == 0) { - data.u[c] = 0; - } else { - data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1]; - } - break; - case GLSL_TYPE_INT: - if (op[1]->value.i[c1] == 0) { - data.i[c] = 0; - } else { - data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1]; - } - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_mod: - /* FINISHME: Emit warning when division-by-zero is detected. */ - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - if (op[1]->value.u[c1] == 0) { - data.u[c] = 0; - } else { - data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1]; - } - break; - case GLSL_TYPE_INT: - if (op[1]->value.i[c1] == 0) { - data.i[c] = 0; - } else { - data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1]; - } - break; - case GLSL_TYPE_FLOAT: - /* We don't use fmod because it rounds toward zero; GLSL specifies - * the use of floor. - */ - data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1] - * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - - break; - - case ir_binop_logic_and: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] && op[1]->value.b[c]; - break; - case ir_binop_logic_xor: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c]; - break; - case ir_binop_logic_or: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] || op[1]->value.b[c]; - break; - - case ir_binop_less: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] < op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] < op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] < op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_greater: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_lequal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_gequal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_equal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < components; c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_nequal: - assert(op[0]->type != op[1]->type); - for (unsigned c = 0; c < components; c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_all_equal: - data.b[0] = op[0]->has_value(op[1]); - break; - case ir_binop_any_nequal: - data.b[0] = !op[0]->has_value(op[1]); - break; - - case ir_binop_lshift: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1]; - } - } - break; - - case ir_binop_rshift: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1]; - } - } - break; - - case ir_binop_bit_and: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_binop_bit_or: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_binop_bit_xor: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_quadop_vector: - for (unsigned c = 0; c < this->type->vector_elements; c++) { - switch (this->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[c]->value.i[0]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[c]->value.u[0]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[c]->value.f[0]; - break; - default: - assert(0); - } - } - break; - - default: - /* FINISHME: Should handle all expression types. */ - return NULL; - } - - return new(ctx) ir_constant(this->type, &data); -} - - -ir_constant * -ir_texture::constant_expression_value() -{ - /* texture lookups aren't constant expressions */ - return NULL; -} - - -ir_constant * -ir_swizzle::constant_expression_value() -{ - ir_constant *v = this->val->constant_expression_value(); - - if (v != NULL) { - ir_constant_data data = { { 0 } }; - - const unsigned swiz_idx[4] = { - this->mask.x, this->mask.y, this->mask.z, this->mask.w - }; - - for (unsigned i = 0; i < this->mask.num_components; i++) { - switch (v->type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break; - case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break; - case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break; - default: assert(!"Should not get here."); break; - } - } - - void *ctx = ralloc_parent(this); - return new(ctx) ir_constant(this->type, &data); - } - return NULL; -} - - -ir_constant * -ir_dereference_variable::constant_expression_value() -{ - /* This may occur during compile and var->type is glsl_type::error_type */ - if (!var) - return NULL; - - /* The constant_value of a uniform variable is its initializer, - * not the lifetime constant value of the uniform. - */ - if (var->mode == ir_var_uniform) - return NULL; - - if (!var->constant_value) - return NULL; - - return var->constant_value->clone(ralloc_parent(var), NULL); -} - - -ir_constant * -ir_dereference_array::constant_expression_value() -{ - ir_constant *array = this->array->constant_expression_value(); - ir_constant *idx = this->array_index->constant_expression_value(); - - if ((array != NULL) && (idx != NULL)) { - void *ctx = ralloc_parent(this); - if (array->type->is_matrix()) { - /* Array access of a matrix results in a vector. - */ - const unsigned column = idx->value.u[0]; - - const glsl_type *const column_type = array->type->column_type(); - - /* Offset in the constant matrix to the first element of the column - * to be extracted. - */ - const unsigned mat_idx = column * column_type->vector_elements; - - ir_constant_data data = { { 0 } }; - - switch (column_type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - for (unsigned i = 0; i < column_type->vector_elements; i++) - data.u[i] = array->value.u[mat_idx + i]; - - break; - - case GLSL_TYPE_FLOAT: - for (unsigned i = 0; i < column_type->vector_elements; i++) - data.f[i] = array->value.f[mat_idx + i]; - - break; - - default: - assert(!"Should not get here."); - break; - } - - return new(ctx) ir_constant(column_type, &data); - } else if (array->type->is_vector()) { - const unsigned component = idx->value.u[0]; - - return new(ctx) ir_constant(array, component); - } else { - const unsigned index = idx->value.u[0]; - return array->get_array_element(index)->clone(ctx, NULL); - } - } - return NULL; -} - - -ir_constant * -ir_dereference_record::constant_expression_value() -{ - ir_constant *v = this->record->constant_expression_value(); - - return (v != NULL) ? v->get_record_field(this->field) : NULL; -} - - -ir_constant * -ir_assignment::constant_expression_value() -{ - /* FINISHME: Handle CEs involving assignment (return RHS) */ - return NULL; -} - - -ir_constant * -ir_constant::constant_expression_value() -{ - return this; -} - - -ir_constant * -ir_call::constant_expression_value() -{ - if (this->type == glsl_type::error_type) - return NULL; - - /* From the GLSL 1.20 spec, page 23: - * "Function calls to user-defined functions (non-built-in functions) - * cannot be used to form constant expressions." - */ - if (!this->callee->is_builtin) - return NULL; - - unsigned num_parameters = 0; - - /* Check if all parameters are constant */ - ir_constant *op[3]; - foreach_list(n, &this->actual_parameters) { - ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value(); - if (constant == NULL) - return NULL; - - op[num_parameters] = constant; - - assert(num_parameters < 3); - num_parameters++; - } - - /* Individual cases below can either: - * - Assign "expr" a new ir_expression to evaluate (for basic opcodes) - * - Fill "data" with appopriate constant data - * - Return an ir_constant directly. - */ - void *mem_ctx = ralloc_parent(this); - ir_expression *expr = NULL; - - ir_constant_data data; - memset(&data, 0, sizeof(data)); - - const char *callee = this->callee_name(); - if (strcmp(callee, "abs") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL); - } else if (strcmp(callee, "all") == 0) { - assert(op[0]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (!op[0]->value.b[c]) - return new(mem_ctx) ir_constant(false); - } - return new(mem_ctx) ir_constant(true); - } else if (strcmp(callee, "any") == 0) { - assert(op[0]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (op[0]->value.b[c]) - return new(mem_ctx) ir_constant(true); - } - return new(mem_ctx) ir_constant(false); - } else if (strcmp(callee, "acos") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = acosf(op[0]->value.f[c]); - } else if (strcmp(callee, "acosh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = acoshf(op[0]->value.f[c]); - } else if (strcmp(callee, "asin") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = asinf(op[0]->value.f[c]); - } else if (strcmp(callee, "asinh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = asinhf(op[0]->value.f[c]); - } else if (strcmp(callee, "atan") == 0) { - assert(op[0]->type->is_float()); - if (num_parameters == 2) { - assert(op[1]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]); - } else { - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atanf(op[0]->value.f[c]); - } - } else if (strcmp(callee, "atanh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atanhf(op[0]->value.f[c]); - } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) { - return ir_constant::zero(mem_ctx, this->type); - } else if (strcmp(callee, "ceil") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL); - } else if (strcmp(callee, "clamp") == 0) { - assert(num_parameters == 3); - unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1; - unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, c1 = 0, c2 = 0; - c < op[0]->type->components(); - c1 += c1_inc, c2 += c2_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1], - op[2]->value.u[c2]); - break; - case GLSL_TYPE_INT: - data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1], - op[2]->value.i[c2]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1], - op[2]->value.f[c2]); - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "cos") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL); - } else if (strcmp(callee, "cosh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = coshf(op[0]->value.f[c]); - } else if (strcmp(callee, "cross") == 0) { - assert(op[0]->type == glsl_type::vec3_type); - assert(op[1]->type == glsl_type::vec3_type); - data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] - - op[1]->value.f[1] * op[0]->value.f[2]); - data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] - - op[1]->value.f[2] * op[0]->value.f[0]); - data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] - - op[1]->value.f[0] * op[0]->value.f[1]); - } else if (strcmp(callee, "degrees") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = 180.0F / M_PI * op[0]->value.f[c]; - } else if (strcmp(callee, "distance") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - float length_squared = 0.0; - for (unsigned c = 0; c < op[0]->type->components(); c++) { - float t = op[0]->value.f[c] - op[1]->value.f[c]; - length_squared += t * t; - } - return new(mem_ctx) ir_constant(sqrtf(length_squared)); - } else if (strcmp(callee, "dot") == 0) { - return new(mem_ctx) ir_constant(dot(op[0], op[1])); - } else if (strcmp(callee, "equal") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; - break; - case GLSL_TYPE_BOOL: - data.b[c] = op[0]->value.b[c] == op[1]->value.b[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "exp") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL); - } else if (strcmp(callee, "exp2") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL); - } else if (strcmp(callee, "faceforward") == 0) { - if (dot(op[2], op[1]) < 0) - return op[0]; - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = -op[0]->value.f[c]; - } else if (strcmp(callee, "floor") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL); - } else if (strcmp(callee, "fract") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL); - } else if (strcmp(callee, "fwidth") == 0) { - return ir_constant::zero(mem_ctx, this->type); - } else if (strcmp(callee, "greaterThan") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "greaterThanEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "inversesqrt") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL); - } else if (strcmp(callee, "length") == 0) { - return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0]))); - } else if (strcmp(callee, "lessThan") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] < op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] < op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] < op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "lessThanEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "log") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL); - } else if (strcmp(callee, "log2") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL); - } else if (strcmp(callee, "matrixCompMult") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] * op[1]->value.f[c]; - } else if (strcmp(callee, "max") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]); - } else if (strcmp(callee, "min") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]); - } else if (strcmp(callee, "mix") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - if (op[2]->type->is_float()) { - unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; - unsigned components = op[0]->type->components(); - for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) { - data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) + - op[1]->value.f[c] * op[2]->value.f[c2]; - } - } else { - assert(op[2]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c]; - } - } else if (strcmp(callee, "mod") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]); - } else if (strcmp(callee, "normalize") == 0) { - assert(op[0]->type->is_float()); - float length = sqrtf(dot(op[0], op[0])); - - if (length == 0) - return ir_constant::zero(mem_ctx, this->type); - - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] / length; - } else if (strcmp(callee, "not") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL); - } else if (strcmp(callee, "notEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; - break; - case GLSL_TYPE_BOOL: - data.b[c] = op[0]->value.b[c] != op[1]->value.b[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "outerProduct") == 0) { - assert(op[0]->type->is_vector() && op[1]->type->is_vector()); - const unsigned m = op[0]->type->vector_elements; - const unsigned n = op[1]->type->vector_elements; - for (unsigned j = 0; j < n; j++) { - for (unsigned i = 0; i < m; i++) { - data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j]; - } - } - } else if (strcmp(callee, "pow") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]); - } else if (strcmp(callee, "radians") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = M_PI / 180.0F * op[0]->value.f[c]; - } else if (strcmp(callee, "reflect") == 0) { - assert(op[0]->type->is_float()); - float dot_NI = dot(op[1], op[0]); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c]; - } else if (strcmp(callee, "refract") == 0) { - const float eta = op[2]->value.f[0]; - const float dot_NI = dot(op[1], op[0]); - const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI); - if (k < 0.0) { - return ir_constant::zero(mem_ctx, this->type); - } else { - for (unsigned c = 0; c < type->components(); c++) { - data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k)) - * op[1]->value.f[c]; - } - } - } else if (strcmp(callee, "sign") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL); - } else if (strcmp(callee, "sin") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL); - } else if (strcmp(callee, "sinh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = sinhf(op[0]->value.f[c]); - } else if (strcmp(callee, "smoothstep") == 0) { - assert(num_parameters == 3); - assert(op[1]->type == op[0]->type); - unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) { - const float edge0 = op[0]->value.f[e]; - const float edge1 = op[1]->value.f[e]; - if (edge0 == edge1) { - data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */ - } else { - const float numerator = op[2]->value.f[c] - edge0; - const float denominator = edge1 - edge0; - const float t = CLAMP(numerator/denominator, 0, 1); - data.f[c] = t * t * (3 - 2 * t); - } - } - } else if (strcmp(callee, "sqrt") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL); - } else if (strcmp(callee, "step") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - /* op[0] (edge) may be either a scalar or a vector */ - const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++) - data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F; - } else if (strcmp(callee, "tan") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = tanf(op[0]->value.f[c]); - } else if (strcmp(callee, "tanh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = tanhf(op[0]->value.f[c]); - } else if (strcmp(callee, "transpose") == 0) { - assert(op[0]->type->is_matrix()); - const unsigned n = op[0]->type->vector_elements; - const unsigned m = op[0]->type->matrix_columns; - for (unsigned j = 0; j < m; j++) { - for (unsigned i = 0; i < n; i++) { - data.f[m*i+j] += op[0]->value.f[i+n*j]; - } - } - } else { - /* Unsupported builtin - some are not allowed in constant expressions. */ - return NULL; - } - - if (expr != NULL) - return expr->constant_expression_value(); - - return new(mem_ctx) ir_constant(this->type, &data); -} +/* + * 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. + */ + +/** + * \file ir_constant_expression.cpp + * Evaluate and process constant valued expressions + * + * In GLSL, constant valued expressions are used in several places. These + * must be processed and evaluated very early in the compilation process. + * + * * Sizes of arrays + * * Initializers for uniforms + * * Initializers for \c const variables + */ + +#include +#include "main/core.h" /* for MAX2, MIN2, CLAMP */ +#include "ir.h" +#include "ir_visitor.h" +#include "glsl_types.h" + +static float +dot(ir_constant *op0, ir_constant *op1) +{ + assert(op0->type->is_float() && op1->type->is_float()); + + float result = 0; + for (unsigned c = 0; c < op0->type->components(); c++) + result += op0->value.f[c] * op1->value.f[c]; + + return result; +} + +ir_constant * +ir_expression::constant_expression_value() +{ + if (this->type->is_error()) + return NULL; + + ir_constant *op[Elements(this->operands)] = { NULL, }; + ir_constant_data data; + + memset(&data, 0, sizeof(data)); + + for (unsigned operand = 0; operand < this->get_num_operands(); operand++) { + op[operand] = this->operands[operand]->constant_expression_value(); + if (!op[operand]) + return NULL; + } + + if (op[1] != NULL) + assert(op[0]->type->base_type == op[1]->type->base_type); + + bool op0_scalar = op[0]->type->is_scalar(); + bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar(); + + /* When iterating over a vector or matrix's components, we want to increase + * the loop counter. However, for scalars, we want to stay at 0. + */ + unsigned c0_inc = op0_scalar ? 0 : 1; + unsigned c1_inc = op1_scalar ? 0 : 1; + unsigned components; + if (op1_scalar || !op[1]) { + components = op[0]->type->components(); + } else { + components = op[1]->type->components(); + } + + void *ctx = ralloc_parent(this); + + /* Handle array operations here, rather than below. */ + if (op[0]->type->is_array()) { + assert(op[1] != NULL && op[1]->type->is_array()); + switch (this->operation) { + case ir_binop_all_equal: + return new(ctx) ir_constant(op[0]->has_value(op[1])); + case ir_binop_any_nequal: + return new(ctx) ir_constant(!op[0]->has_value(op[1])); + default: + break; + } + return NULL; + } + + switch (this->operation) { + case ir_unop_bit_not: + switch (op[0]->type->base_type) { + case GLSL_TYPE_INT: + for (unsigned c = 0; c < components; c++) + data.i[c] = ~ op[0]->value.i[c]; + break; + case GLSL_TYPE_UINT: + for (unsigned c = 0; c < components; c++) + data.u[c] = ~ op[0]->value.u[c]; + break; + default: + assert(0); + } + break; + + case ir_unop_logic_not: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.b[c] = !op[0]->value.b[c]; + break; + + case ir_unop_f2i: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.i[c] = (int) op[0]->value.f[c]; + } + break; + case ir_unop_i2f: + assert(op[0]->type->base_type == GLSL_TYPE_INT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = (float) op[0]->value.i[c]; + } + break; + case ir_unop_u2f: + assert(op[0]->type->base_type == GLSL_TYPE_UINT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = (float) op[0]->value.u[c]; + } + break; + case ir_unop_b2f: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F; + } + break; + case ir_unop_f2b: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.b[c] = op[0]->value.f[c] != 0.0F ? true : false; + } + break; + case ir_unop_b2i: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.u[c] = op[0]->value.b[c] ? 1 : 0; + } + break; + case ir_unop_i2b: + assert(op[0]->type->is_integer()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.b[c] = op[0]->value.u[c] ? true : false; + } + break; + case ir_unop_u2i: + assert(op[0]->type->base_type == GLSL_TYPE_UINT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.i[c] = op[0]->value.u[c]; + } + break; + case ir_unop_i2u: + assert(op[0]->type->base_type == GLSL_TYPE_INT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.u[c] = op[0]->value.i[c]; + } + break; + case ir_unop_any: + assert(op[0]->type->is_boolean()); + data.b[0] = false; + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (op[0]->value.b[c]) + data.b[0] = true; + } + break; + + case ir_unop_trunc: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = truncf(op[0]->value.f[c]); + } + break; + + case ir_unop_ceil: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = ceilf(op[0]->value.f[c]); + } + break; + + case ir_unop_floor: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = floorf(op[0]->value.f[c]); + } + break; + + case ir_unop_fract: + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = 0; + break; + case GLSL_TYPE_INT: + data.i[c] = 0; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]); + break; + default: + assert(0); + } + } + break; + + case ir_unop_sin: + case ir_unop_sin_reduced: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = sinf(op[0]->value.f[c]); + } + break; + + case ir_unop_cos: + case ir_unop_cos_reduced: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = cosf(op[0]->value.f[c]); + } + break; + + case ir_unop_neg: + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = -((int) op[0]->value.u[c]); + break; + case GLSL_TYPE_INT: + data.i[c] = -op[0]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = -op[0]->value.f[c]; + break; + default: + assert(0); + } + } + break; + + case ir_unop_abs: + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c]; + if (data.i[c] < 0) + data.i[c] = -data.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = fabs(op[0]->value.f[c]); + break; + default: + assert(0); + } + } + break; + + case ir_unop_sign: + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.i[c] > 0; + break; + case GLSL_TYPE_INT: + data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0); + break; + case GLSL_TYPE_FLOAT: + data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0)); + break; + default: + assert(0); + } + } + break; + + case ir_unop_rcp: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (this->type->base_type) { + case GLSL_TYPE_UINT: + if (op[0]->value.u[c] != 0.0) + data.u[c] = 1 / op[0]->value.u[c]; + break; + case GLSL_TYPE_INT: + if (op[0]->value.i[c] != 0.0) + data.i[c] = 1 / op[0]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + if (op[0]->value.f[c] != 0.0) + data.f[c] = 1.0F / op[0]->value.f[c]; + break; + default: + assert(0); + } + } + break; + + case ir_unop_rsq: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = 1.0F / sqrtf(op[0]->value.f[c]); + } + break; + + case ir_unop_sqrt: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = sqrtf(op[0]->value.f[c]); + } + break; + + case ir_unop_exp: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = expf(op[0]->value.f[c]); + } + break; + + case ir_unop_exp2: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = exp2f(op[0]->value.f[c]); + } + break; + + case ir_unop_log: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = logf(op[0]->value.f[c]); + } + break; + + case ir_unop_log2: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = log2f(op[0]->value.f[c]); + } + break; + + case ir_unop_dFdx: + case ir_unop_dFdy: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = 0.0; + } + break; + + case ir_binop_pow: + assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]); + } + break; + + case ir_binop_dot: + data.f[0] = dot(op[0], op[1]); + break; + + case ir_binop_min: + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]); + break; + case GLSL_TYPE_INT: + data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]); + break; + case GLSL_TYPE_FLOAT: + data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]); + break; + default: + assert(0); + } + } + + break; + case ir_binop_max: + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]); + break; + case GLSL_TYPE_INT: + data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]); + break; + case GLSL_TYPE_FLOAT: + data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]); + break; + default: + assert(0); + } + } + break; + + case ir_binop_add: + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1]; + break; + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1]; + break; + default: + assert(0); + } + } + + break; + case ir_binop_sub: + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1]; + break; + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]; + break; + default: + assert(0); + } + } + + break; + case ir_binop_mul: + /* Check for equal types, or unequal types involving scalars */ + if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix()) + || op0_scalar || op1_scalar) { + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1]; + break; + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1]; + break; + default: + assert(0); + } + } + } else { + assert(op[0]->type->is_matrix() || op[1]->type->is_matrix()); + + /* Multiply an N-by-M matrix with an M-by-P matrix. Since either + * matrix can be a GLSL vector, either N or P can be 1. + * + * For vec*mat, the vector is treated as a row vector. This + * means the vector is a 1-row x M-column matrix. + * + * For mat*vec, the vector is treated as a column vector. Since + * matrix_columns is 1 for vectors, this just works. + */ + const unsigned n = op[0]->type->is_vector() + ? 1 : op[0]->type->vector_elements; + const unsigned m = op[1]->type->vector_elements; + const unsigned p = op[1]->type->matrix_columns; + for (unsigned j = 0; j < p; j++) { + for (unsigned i = 0; i < n; i++) { + for (unsigned k = 0; k < m; k++) { + data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j]; + } + } + } + } + + break; + case ir_binop_div: + /* FINISHME: Emit warning when division-by-zero is detected. */ + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + if (op[1]->value.u[c1] == 0) { + data.u[c] = 0; + } else { + data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1]; + } + break; + case GLSL_TYPE_INT: + if (op[1]->value.i[c1] == 0) { + data.i[c] = 0; + } else { + data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1]; + } + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1]; + break; + default: + assert(0); + } + } + + break; + case ir_binop_mod: + /* FINISHME: Emit warning when division-by-zero is detected. */ + assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + if (op[1]->value.u[c1] == 0) { + data.u[c] = 0; + } else { + data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1]; + } + break; + case GLSL_TYPE_INT: + if (op[1]->value.i[c1] == 0) { + data.i[c] = 0; + } else { + data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1]; + } + break; + case GLSL_TYPE_FLOAT: + /* We don't use fmod because it rounds toward zero; GLSL specifies + * the use of floor. + */ + data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1] + * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]); + break; + default: + assert(0); + } + } + + break; + + case ir_binop_logic_and: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.b[c] = op[0]->value.b[c] && op[1]->value.b[c]; + break; + case ir_binop_logic_xor: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c]; + break; + case ir_binop_logic_or: + assert(op[0]->type->base_type == GLSL_TYPE_BOOL); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.b[c] = op[0]->value.b[c] || op[1]->value.b[c]; + break; + + case ir_binop_less: + assert(op[0]->type == op[1]->type); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[0] = op[0]->value.u[0] < op[1]->value.u[0]; + break; + case GLSL_TYPE_INT: + data.b[0] = op[0]->value.i[0] < op[1]->value.i[0]; + break; + case GLSL_TYPE_FLOAT: + data.b[0] = op[0]->value.f[0] < op[1]->value.f[0]; + break; + default: + assert(0); + } + } + break; + case ir_binop_greater: + assert(op[0]->type == op[1]->type); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; + break; + default: + assert(0); + } + } + break; + case ir_binop_lequal: + assert(op[0]->type == op[1]->type); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0]; + break; + case GLSL_TYPE_INT: + data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0]; + break; + case GLSL_TYPE_FLOAT: + data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0]; + break; + default: + assert(0); + } + } + break; + case ir_binop_gequal: + assert(op[0]->type == op[1]->type); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0]; + break; + case GLSL_TYPE_INT: + data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0]; + break; + case GLSL_TYPE_FLOAT: + data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0]; + break; + default: + assert(0); + } + } + break; + case ir_binop_equal: + assert(op[0]->type == op[1]->type); + for (unsigned c = 0; c < components; c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; + break; + default: + assert(0); + } + } + break; + case ir_binop_nequal: + assert(op[0]->type != op[1]->type); + for (unsigned c = 0; c < components; c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; + break; + default: + assert(0); + } + } + break; + case ir_binop_all_equal: + data.b[0] = op[0]->has_value(op[1]); + break; + case ir_binop_any_nequal: + data.b[0] = !op[0]->has_value(op[1]); + break; + + case ir_binop_lshift: + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + if (op[0]->type->base_type == GLSL_TYPE_INT && + op[1]->type->base_type == GLSL_TYPE_INT) { + data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_INT && + op[1]->type->base_type == GLSL_TYPE_UINT) { + data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_UINT && + op[1]->type->base_type == GLSL_TYPE_INT) { + data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_UINT && + op[1]->type->base_type == GLSL_TYPE_UINT) { + data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1]; + } + } + break; + + case ir_binop_rshift: + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + if (op[0]->type->base_type == GLSL_TYPE_INT && + op[1]->type->base_type == GLSL_TYPE_INT) { + data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_INT && + op[1]->type->base_type == GLSL_TYPE_UINT) { + data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_UINT && + op[1]->type->base_type == GLSL_TYPE_INT) { + data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1]; + + } else if (op[0]->type->base_type == GLSL_TYPE_UINT && + op[1]->type->base_type == GLSL_TYPE_UINT) { + data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1]; + } + } + break; + + case ir_binop_bit_and: + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1]; + break; + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1]; + break; + default: + assert(0); + } + } + break; + + case ir_binop_bit_or: + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1]; + break; + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1]; + break; + default: + assert(0); + } + } + break; + + case ir_binop_bit_xor: + for (unsigned c = 0, c0 = 0, c1 = 0; + c < components; + c0 += c0_inc, c1 += c1_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_INT: + data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1]; + break; + case GLSL_TYPE_UINT: + data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1]; + break; + default: + assert(0); + } + } + break; + + case ir_quadop_vector: + for (unsigned c = 0; c < this->type->vector_elements; c++) { + switch (this->type->base_type) { + case GLSL_TYPE_INT: + data.i[c] = op[c]->value.i[0]; + break; + case GLSL_TYPE_UINT: + data.u[c] = op[c]->value.u[0]; + break; + case GLSL_TYPE_FLOAT: + data.f[c] = op[c]->value.f[0]; + break; + default: + assert(0); + } + } + break; + + default: + /* FINISHME: Should handle all expression types. */ + return NULL; + } + + return new(ctx) ir_constant(this->type, &data); +} + + +ir_constant * +ir_texture::constant_expression_value() +{ + /* texture lookups aren't constant expressions */ + return NULL; +} + + +ir_constant * +ir_swizzle::constant_expression_value() +{ + ir_constant *v = this->val->constant_expression_value(); + + if (v != NULL) { + ir_constant_data data = { { 0 } }; + + const unsigned swiz_idx[4] = { + this->mask.x, this->mask.y, this->mask.z, this->mask.w + }; + + for (unsigned i = 0; i < this->mask.num_components; i++) { + switch (v->type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break; + case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break; + case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break; + default: assert(!"Should not get here."); break; + } + } + + void *ctx = ralloc_parent(this); + return new(ctx) ir_constant(this->type, &data); + } + return NULL; +} + + +ir_constant * +ir_dereference_variable::constant_expression_value() +{ + /* This may occur during compile and var->type is glsl_type::error_type */ + if (!var) + return NULL; + + /* The constant_value of a uniform variable is its initializer, + * not the lifetime constant value of the uniform. + */ + if (var->mode == ir_var_uniform) + return NULL; + + if (!var->constant_value) + return NULL; + + return var->constant_value->clone(ralloc_parent(var), NULL); +} + + +ir_constant * +ir_dereference_array::constant_expression_value() +{ + ir_constant *array = this->array->constant_expression_value(); + ir_constant *idx = this->array_index->constant_expression_value(); + + if ((array != NULL) && (idx != NULL)) { + void *ctx = ralloc_parent(this); + if (array->type->is_matrix()) { + /* Array access of a matrix results in a vector. + */ + const unsigned column = idx->value.u[0]; + + const glsl_type *const column_type = array->type->column_type(); + + /* Offset in the constant matrix to the first element of the column + * to be extracted. + */ + const unsigned mat_idx = column * column_type->vector_elements; + + ir_constant_data data = { { 0 } }; + + switch (column_type->base_type) { + case GLSL_TYPE_UINT: + case GLSL_TYPE_INT: + for (unsigned i = 0; i < column_type->vector_elements; i++) + data.u[i] = array->value.u[mat_idx + i]; + + break; + + case GLSL_TYPE_FLOAT: + for (unsigned i = 0; i < column_type->vector_elements; i++) + data.f[i] = array->value.f[mat_idx + i]; + + break; + + default: + assert(!"Should not get here."); + break; + } + + return new(ctx) ir_constant(column_type, &data); + } else if (array->type->is_vector()) { + const unsigned component = idx->value.u[0]; + + return new(ctx) ir_constant(array, component); + } else { + const unsigned index = idx->value.u[0]; + return array->get_array_element(index)->clone(ctx, NULL); + } + } + return NULL; +} + + +ir_constant * +ir_dereference_record::constant_expression_value() +{ + ir_constant *v = this->record->constant_expression_value(); + + return (v != NULL) ? v->get_record_field(this->field) : NULL; +} + + +ir_constant * +ir_assignment::constant_expression_value() +{ + /* FINISHME: Handle CEs involving assignment (return RHS) */ + return NULL; +} + + +ir_constant * +ir_constant::constant_expression_value() +{ + return this; +} + + +ir_constant * +ir_call::constant_expression_value() +{ + if (this->type == glsl_type::error_type) + return NULL; + + /* From the GLSL 1.20 spec, page 23: + * "Function calls to user-defined functions (non-built-in functions) + * cannot be used to form constant expressions." + */ + if (!this->callee->is_builtin) + return NULL; + + unsigned num_parameters = 0; + + /* Check if all parameters are constant */ + ir_constant *op[3]; + foreach_list(n, &this->actual_parameters) { + ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value(); + if (constant == NULL) + return NULL; + + op[num_parameters] = constant; + + assert(num_parameters < 3); + num_parameters++; + } + + /* Individual cases below can either: + * - Assign "expr" a new ir_expression to evaluate (for basic opcodes) + * - Fill "data" with appopriate constant data + * - Return an ir_constant directly. + */ + void *mem_ctx = ralloc_parent(this); + ir_expression *expr = NULL; + + ir_constant_data data; + memset(&data, 0, sizeof(data)); + + const char *callee = this->callee_name(); + if (strcmp(callee, "abs") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL); + } else if (strcmp(callee, "all") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (!op[0]->value.b[c]) + return new(mem_ctx) ir_constant(false); + } + return new(mem_ctx) ir_constant(true); + } else if (strcmp(callee, "any") == 0) { + assert(op[0]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + if (op[0]->value.b[c]) + return new(mem_ctx) ir_constant(true); + } + return new(mem_ctx) ir_constant(false); + } else if (strcmp(callee, "acos") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = acosf(op[0]->value.f[c]); + } else if (strcmp(callee, "acosh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = acoshf(op[0]->value.f[c]); + } else if (strcmp(callee, "asin") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = asinf(op[0]->value.f[c]); + } else if (strcmp(callee, "asinh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = asinhf(op[0]->value.f[c]); + } else if (strcmp(callee, "atan") == 0) { + assert(op[0]->type->is_float()); + if (num_parameters == 2) { + assert(op[1]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]); + } else { + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = atanf(op[0]->value.f[c]); + } + } else if (strcmp(callee, "atanh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = atanhf(op[0]->value.f[c]); + } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) { + return ir_constant::zero(mem_ctx, this->type); + } else if (strcmp(callee, "ceil") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL); + } else if (strcmp(callee, "clamp") == 0) { + assert(num_parameters == 3); + unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1; + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; + for (unsigned c = 0, c1 = 0, c2 = 0; + c < op[0]->type->components(); + c1 += c1_inc, c2 += c2_inc, c++) { + + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1], + op[2]->value.u[c2]); + break; + case GLSL_TYPE_INT: + data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1], + op[2]->value.i[c2]); + break; + case GLSL_TYPE_FLOAT: + data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1], + op[2]->value.f[c2]); + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "cos") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL); + } else if (strcmp(callee, "cosh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = coshf(op[0]->value.f[c]); + } else if (strcmp(callee, "cross") == 0) { + assert(op[0]->type == glsl_type::vec3_type); + assert(op[1]->type == glsl_type::vec3_type); + data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] - + op[1]->value.f[1] * op[0]->value.f[2]); + data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] - + op[1]->value.f[2] * op[0]->value.f[0]); + data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] - + op[1]->value.f[0] * op[0]->value.f[1]); + } else if (strcmp(callee, "degrees") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = 180.0F / M_PI * op[0]->value.f[c]; + } else if (strcmp(callee, "distance") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + float length_squared = 0.0; + for (unsigned c = 0; c < op[0]->type->components(); c++) { + float t = op[0]->value.f[c] - op[1]->value.f[c]; + length_squared += t * t; + } + return new(mem_ctx) ir_constant(sqrtf(length_squared)); + } else if (strcmp(callee, "dot") == 0) { + return new(mem_ctx) ir_constant(dot(op[0], op[1])); + } else if (strcmp(callee, "equal") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; + break; + case GLSL_TYPE_BOOL: + data.b[c] = op[0]->value.b[c] == op[1]->value.b[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "exp") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL); + } else if (strcmp(callee, "exp2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL); + } else if (strcmp(callee, "faceforward") == 0) { + if (dot(op[2], op[1]) < 0) + return op[0]; + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = -op[0]->value.f[c]; + } else if (strcmp(callee, "floor") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL); + } else if (strcmp(callee, "fract") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL); + } else if (strcmp(callee, "fwidth") == 0) { + return ir_constant::zero(mem_ctx, this->type); + } else if (strcmp(callee, "greaterThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "greaterThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "inversesqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL); + } else if (strcmp(callee, "length") == 0) { + return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0]))); + } else if (strcmp(callee, "lessThan") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] < op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] < op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] < op[1]->value.f[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "lessThanEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "log") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL); + } else if (strcmp(callee, "log2") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL); + } else if (strcmp(callee, "matrixCompMult") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] * op[1]->value.f[c]; + } else if (strcmp(callee, "max") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]); + } else if (strcmp(callee, "min") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]); + } else if (strcmp(callee, "mix") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + if (op[2]->type->is_float()) { + unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; + unsigned components = op[0]->type->components(); + for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) { + data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) + + op[1]->value.f[c] * op[2]->value.f[c2]; + } + } else { + assert(op[2]->type->is_boolean()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c]; + } + } else if (strcmp(callee, "mod") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]); + } else if (strcmp(callee, "normalize") == 0) { + assert(op[0]->type->is_float()); + float length = sqrtf(dot(op[0], op[0])); + + if (length == 0) + return ir_constant::zero(mem_ctx, this->type); + + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] / length; + } else if (strcmp(callee, "not") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL); + } else if (strcmp(callee, "notEqual") == 0) { + assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); + for (unsigned c = 0; c < op[0]->type->components(); c++) { + switch (op[0]->type->base_type) { + case GLSL_TYPE_UINT: + data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; + break; + case GLSL_TYPE_INT: + data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; + break; + case GLSL_TYPE_FLOAT: + data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; + break; + case GLSL_TYPE_BOOL: + data.b[c] = op[0]->value.b[c] != op[1]->value.b[c]; + break; + default: + assert(!"Should not get here."); + } + } + } else if (strcmp(callee, "outerProduct") == 0) { + assert(op[0]->type->is_vector() && op[1]->type->is_vector()); + const unsigned m = op[0]->type->vector_elements; + const unsigned n = op[1]->type->vector_elements; + for (unsigned j = 0; j < n; j++) { + for (unsigned i = 0; i < m; i++) { + data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j]; + } + } + } else if (strcmp(callee, "pow") == 0) { + expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]); + } else if (strcmp(callee, "radians") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = M_PI / 180.0F * op[0]->value.f[c]; + } else if (strcmp(callee, "reflect") == 0) { + assert(op[0]->type->is_float()); + float dot_NI = dot(op[1], op[0]); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c]; + } else if (strcmp(callee, "refract") == 0) { + const float eta = op[2]->value.f[0]; + const float dot_NI = dot(op[1], op[0]); + const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI); + if (k < 0.0) { + return ir_constant::zero(mem_ctx, this->type); + } else { + for (unsigned c = 0; c < type->components(); c++) { + data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k)) + * op[1]->value.f[c]; + } + } + } else if (strcmp(callee, "sign") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL); + } else if (strcmp(callee, "sin") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL); + } else if (strcmp(callee, "sinh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = sinhf(op[0]->value.f[c]); + } else if (strcmp(callee, "smoothstep") == 0) { + assert(num_parameters == 3); + assert(op[1]->type == op[0]->type); + unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1; + for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) { + const float edge0 = op[0]->value.f[e]; + const float edge1 = op[1]->value.f[e]; + if (edge0 == edge1) { + data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */ + } else { + const float numerator = op[2]->value.f[c] - edge0; + const float denominator = edge1 - edge0; + const float t = CLAMP(numerator/denominator, 0, 1); + data.f[c] = t * t * (3 - 2 * t); + } + } + } else if (strcmp(callee, "sqrt") == 0) { + expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL); + } else if (strcmp(callee, "step") == 0) { + assert(op[0]->type->is_float() && op[1]->type->is_float()); + /* op[0] (edge) may be either a scalar or a vector */ + const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1; + for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++) + data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F; + } else if (strcmp(callee, "tan") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanf(op[0]->value.f[c]); + } else if (strcmp(callee, "tanh") == 0) { + assert(op[0]->type->is_float()); + for (unsigned c = 0; c < op[0]->type->components(); c++) + data.f[c] = tanhf(op[0]->value.f[c]); + } else if (strcmp(callee, "transpose") == 0) { + assert(op[0]->type->is_matrix()); + const unsigned n = op[0]->type->vector_elements; + const unsigned m = op[0]->type->matrix_columns; + for (unsigned j = 0; j < m; j++) { + for (unsigned i = 0; i < n; i++) { + data.f[m*i+j] += op[0]->value.f[i+n*j]; + } + } + } else { + /* Unsupported builtin - some are not allowed in constant expressions. */ + return NULL; + } + + if (expr != NULL) + return expr->constant_expression_value(); + + return new(mem_ctx) ir_constant(this->type, &data); +} diff --git a/mesalib/src/glsl/ir_import_prototypes.cpp b/mesalib/src/glsl/ir_import_prototypes.cpp index 03d40bb68..3585bf6b2 100644 --- a/mesalib/src/glsl/ir_import_prototypes.cpp +++ b/mesalib/src/glsl/ir_import_prototypes.cpp @@ -1,122 +1,122 @@ -/* - * 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. - */ - -/** - * \file ir_import_prototypes.cpp - * Import function prototypes from one IR tree into another. - * - * \author Ian Romanick - */ -#include "ir.h" -#include "glsl_symbol_table.h" - -/** - * Visitor used to import function prototypes - * - * Normally the \c clone method of either \c ir_function or - * \c ir_function_signature could be used. However, we don't want a complete - * clone of the \c ir_function_signature. We want everything \b except the - * body of the function. - */ -class import_prototype_visitor : public ir_hierarchical_visitor { -public: - /** - */ - import_prototype_visitor(exec_list *list, glsl_symbol_table *symbols, - void *mem_ctx) - { - this->mem_ctx = mem_ctx; - this->list = list; - this->symbols = symbols; - this->function = NULL; - } - - virtual ir_visitor_status visit_enter(ir_function *ir) - { - assert(this->function == NULL); - - this->function = this->symbols->get_function(ir->name); - if (!this->function) { - this->function = new(this->mem_ctx) ir_function(ir->name); - - list->push_tail(this->function); - - /* Add the new function to the symbol table. - */ - this->symbols->add_function(this->function); - } - return visit_continue; - } - - virtual ir_visitor_status visit_leave(ir_function *ir) - { - (void) ir; - assert(this->function != NULL); - - this->function = NULL; - return visit_continue; - } - - ir_visitor_status visit_enter(ir_function_signature *ir) - { - assert(this->function != NULL); - - ir_function_signature *copy = ir->clone_prototype(mem_ctx, NULL); - - this->function->add_signature(copy); - - /* Do not process child nodes of the ir_function_signature. There can - * never be any nodes inside the ir_function_signature that we care - * about. Instead continue with the next sibling. - */ - return visit_continue_with_parent; - } - -private: - exec_list *list; - ir_function *function; - glsl_symbol_table *symbols; - void *mem_ctx; -}; - - -/** - * Import function prototypes from one IR tree into another - * - * \param source Source instruction stream containing functions whose - * prototypes are to be imported - * \param dest Destination instruction stream where new \c ir_function and - * \c ir_function_signature nodes will be stored - * \param symbols Symbol table where new functions will be stored - * \param mem_ctx ralloc memory context used for new allocations - */ -void -import_prototypes(const exec_list *source, exec_list *dest, - glsl_symbol_table *symbols, void *mem_ctx) -{ - import_prototype_visitor v(dest, symbols, mem_ctx); - - /* Making source be const is just extra documentation. - */ - v.run(const_cast(source)); -} +/* + * 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. + */ + +/** + * \file ir_import_prototypes.cpp + * Import function prototypes from one IR tree into another. + * + * \author Ian Romanick + */ +#include "ir.h" +#include "glsl_symbol_table.h" + +/** + * Visitor used to import function prototypes + * + * Normally the \c clone method of either \c ir_function or + * \c ir_function_signature could be used. However, we don't want a complete + * clone of the \c ir_function_signature. We want everything \b except the + * body of the function. + */ +class import_prototype_visitor : public ir_hierarchical_visitor { +public: + /** + */ + import_prototype_visitor(exec_list *list, glsl_symbol_table *symbols, + void *mem_ctx) + { + this->mem_ctx = mem_ctx; + this->list = list; + this->symbols = symbols; + this->function = NULL; + } + + virtual ir_visitor_status visit_enter(ir_function *ir) + { + assert(this->function == NULL); + + this->function = this->symbols->get_function(ir->name); + if (!this->function) { + this->function = new(this->mem_ctx) ir_function(ir->name); + + list->push_tail(this->function); + + /* Add the new function to the symbol table. + */ + this->symbols->add_function(this->function); + } + return visit_continue; + } + + virtual ir_visitor_status visit_leave(ir_function *ir) + { + (void) ir; + assert(this->function != NULL); + + this->function = NULL; + return visit_continue; + } + + ir_visitor_status visit_enter(ir_function_signature *ir) + { + assert(this->function != NULL); + + ir_function_signature *copy = ir->clone_prototype(mem_ctx, NULL); + + this->function->add_signature(copy); + + /* Do not process child nodes of the ir_function_signature. There can + * never be any nodes inside the ir_function_signature that we care + * about. Instead continue with the next sibling. + */ + return visit_continue_with_parent; + } + +private: + exec_list *list; + ir_function *function; + glsl_symbol_table *symbols; + void *mem_ctx; +}; + + +/** + * Import function prototypes from one IR tree into another + * + * \param source Source instruction stream containing functions whose + * prototypes are to be imported + * \param dest Destination instruction stream where new \c ir_function and + * \c ir_function_signature nodes will be stored + * \param symbols Symbol table where new functions will be stored + * \param mem_ctx ralloc memory context used for new allocations + */ +void +import_prototypes(const exec_list *source, exec_list *dest, + glsl_symbol_table *symbols, void *mem_ctx) +{ + import_prototype_visitor v(dest, symbols, mem_ctx); + + /* Making source be const is just extra documentation. + */ + v.run(const_cast(source)); +} diff --git a/mesalib/src/glsl/ir_print_visitor.cpp b/mesalib/src/glsl/ir_print_visitor.cpp index 89f20529e..ea7858224 100644 --- a/mesalib/src/glsl/ir_print_visitor.cpp +++ b/mesalib/src/glsl/ir_print_visitor.cpp @@ -1,528 +1,528 @@ -/* - * 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 "ir_print_visitor.h" -#include "glsl_types.h" -#include "glsl_parser_extras.h" - -extern "C" { -#include "program/hash_table.h" -} - -static void print_type(const glsl_type *t); - -void -ir_instruction::print(void) const -{ - ir_instruction *deconsted = const_cast(this); - - ir_print_visitor v; - deconsted->accept(&v); -} - -void -_mesa_print_ir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - if (state) { - for (unsigned i = 0; i < state->num_user_structures; i++) { - const glsl_type *const s = state->user_structures[i]; - - printf("(structure (%s) (%s@%p) (%u) (\n", - s->name, s->name, (void *) s, s->length); - - for (unsigned j = 0; j < s->length; j++) { - printf("\t(("); - print_type(s->fields.structure[j].type); - printf(")(%s))\n", s->fields.structure[j].name); - } - - printf(")\n"); - } - } - - printf("(\n"); - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir->print(); - if (ir->ir_type != ir_type_function) - printf("\n"); - } - printf("\n)"); -} - -ir_print_visitor::ir_print_visitor() -{ - indentation = 0; - printable_names = - hash_table_ctor(32, hash_table_pointer_hash, hash_table_pointer_compare); - symbols = _mesa_symbol_table_ctor(); - mem_ctx = ralloc_context(NULL); -} - -ir_print_visitor::~ir_print_visitor() -{ - hash_table_dtor(printable_names); - _mesa_symbol_table_dtor(symbols); - ralloc_free(mem_ctx); -} - -void ir_print_visitor::indent(void) -{ - for (int i = 0; i < indentation; i++) - printf(" "); -} - -const char * -ir_print_visitor::unique_name(ir_variable *var) -{ - /* var->name can be NULL in function prototypes when a type is given for a - * parameter but no name is given. In that case, just return an empty - * string. Don't worry about tracking the generated name in the printable - * names hash because this is the only scope where it can ever appear. - */ - if (var->name == NULL) { - static unsigned arg = 1; - return ralloc_asprintf(this->mem_ctx, "parameter@%u", arg++); - } - - /* Do we already have a name for this variable? */ - const char *name = (const char *) hash_table_find(this->printable_names, var); - if (name != NULL) - return name; - - /* If there's no conflict, just use the original name */ - if (_mesa_symbol_table_find_symbol(this->symbols, -1, var->name) == NULL) { - name = var->name; - } else { - static unsigned i = 1; - name = ralloc_asprintf(this->mem_ctx, "%s@%u", var->name, ++i); - } - hash_table_insert(this->printable_names, (void *) name, var); - _mesa_symbol_table_add_symbol(this->symbols, -1, name, var); - return name; -} - -static void -print_type(const glsl_type *t) -{ - if (t->base_type == GLSL_TYPE_ARRAY) { - printf("(array "); - print_type(t->fields.array); - printf(" %u)", t->length); - } else if ((t->base_type == GLSL_TYPE_STRUCT) - && (strncmp("gl_", t->name, 3) != 0)) { - printf("%s@%p", t->name, (void *) t); - } else { - printf("%s", t->name); - } -} - - -void ir_print_visitor::visit(ir_variable *ir) -{ - printf("(declare "); - - const char *const cent = (ir->centroid) ? "centroid " : ""; - const char *const inv = (ir->invariant) ? "invariant " : ""; - const char *const mode[] = { "", "uniform ", "in ", "out ", "inout ", - "const_in ", "sys ", "temporary " }; - const char *const interp[] = { "", "flat", "noperspective" }; - - printf("(%s%s%s%s) ", - cent, inv, mode[ir->mode], interp[ir->interpolation]); - - print_type(ir->type); - printf(" %s)", unique_name(ir)); -} - - -void ir_print_visitor::visit(ir_function_signature *ir) -{ - _mesa_symbol_table_push_scope(symbols); - printf("(signature "); - indentation++; - - print_type(ir->return_type); - printf("\n"); - indent(); - - printf("(parameters\n"); - indentation++; - - foreach_iter(exec_list_iterator, iter, ir->parameters) { - ir_variable *const inst = (ir_variable *) iter.get(); - - indent(); - inst->accept(this); - printf("\n"); - } - indentation--; - - indent(); - printf(")\n"); - - indent(); - - printf("(\n"); - indentation++; - - foreach_iter(exec_list_iterator, iter, ir->body) { - ir_instruction *const inst = (ir_instruction *) iter.get(); - - indent(); - inst->accept(this); - printf("\n"); - } - indentation--; - indent(); - printf("))\n"); - indentation--; - _mesa_symbol_table_pop_scope(symbols); -} - - -void ir_print_visitor::visit(ir_function *ir) -{ - printf("(function %s\n", ir->name); - indentation++; - foreach_iter(exec_list_iterator, iter, *ir) { - ir_function_signature *const sig = (ir_function_signature *) iter.get(); - indent(); - sig->accept(this); - printf("\n"); - } - indentation--; - indent(); - printf(")\n\n"); -} - - -void ir_print_visitor::visit(ir_expression *ir) -{ - printf("(expression "); - - print_type(ir->type); - - printf(" %s ", ir->operator_string()); - - for (unsigned i = 0; i < ir->get_num_operands(); i++) { - ir->operands[i]->accept(this); - } - - printf(") "); -} - - -void ir_print_visitor::visit(ir_texture *ir) -{ - printf("(%s ", ir->opcode_string()); - - print_type(ir->type); - printf(" "); - - ir->sampler->accept(this); - printf(" "); - - if (ir->op != ir_txs) { - ir->coordinate->accept(this); - - printf(" "); - - if (ir->offset != NULL) { - ir->offset->accept(this); - } else { - printf("0"); - } - - printf(" "); - } - - if (ir->op != ir_txf && ir->op != ir_txs) { - if (ir->projector) - ir->projector->accept(this); - else - printf("1"); - - if (ir->shadow_comparitor) { - printf(" "); - ir->shadow_comparitor->accept(this); - } else { - printf(" ()"); - } - } - - printf(" "); - switch (ir->op) - { - case ir_tex: - break; - case ir_txb: - ir->lod_info.bias->accept(this); - break; - case ir_txl: - case ir_txf: - case ir_txs: - ir->lod_info.lod->accept(this); - break; - case ir_txd: - printf("("); - ir->lod_info.grad.dPdx->accept(this); - printf(" "); - ir->lod_info.grad.dPdy->accept(this); - printf(")"); - break; - }; - printf(")"); -} - - -void ir_print_visitor::visit(ir_swizzle *ir) -{ - const unsigned swiz[4] = { - ir->mask.x, - ir->mask.y, - ir->mask.z, - ir->mask.w, - }; - - printf("(swiz "); - for (unsigned i = 0; i < ir->mask.num_components; i++) { - printf("%c", "xyzw"[swiz[i]]); - } - printf(" "); - ir->val->accept(this); - printf(")"); -} - - -void ir_print_visitor::visit(ir_dereference_variable *ir) -{ - ir_variable *var = ir->variable_referenced(); - printf("(var_ref %s) ", unique_name(var)); -} - - -void ir_print_visitor::visit(ir_dereference_array *ir) -{ - printf("(array_ref "); - ir->array->accept(this); - ir->array_index->accept(this); - printf(") "); -} - - -void ir_print_visitor::visit(ir_dereference_record *ir) -{ - printf("(record_ref "); - ir->record->accept(this); - printf(" %s) ", ir->field); -} - - -void ir_print_visitor::visit(ir_assignment *ir) -{ - printf("(assign "); - - if (ir->condition) - ir->condition->accept(this); - - char mask[5]; - unsigned j = 0; - - for (unsigned i = 0; i < 4; i++) { - if ((ir->write_mask & (1 << i)) != 0) { - mask[j] = "xyzw"[i]; - j++; - } - } - mask[j] = '\0'; - - printf(" (%s) ", mask); - - ir->lhs->accept(this); - - printf(" "); - - ir->rhs->accept(this); - printf(") "); -} - - -void ir_print_visitor::visit(ir_constant *ir) -{ - const glsl_type *const base_type = ir->type->get_base_type(); - - printf("(constant "); - print_type(ir->type); - printf(" ("); - - if (ir->type->is_array()) { - for (unsigned i = 0; i < ir->type->length; i++) - ir->get_array_element(i)->accept(this); - } else if (ir->type->is_record()) { - ir_constant *value = (ir_constant *) ir->components.get_head(); - for (unsigned i = 0; i < ir->type->length; i++) { - printf("(%s ", ir->type->fields.structure[i].name); - value->accept(this); - printf(")"); - - value = (ir_constant *) value->next; - } - } else { - for (unsigned i = 0; i < ir->type->components(); i++) { - if (i != 0) - printf(" "); - switch (base_type->base_type) { - case GLSL_TYPE_UINT: printf("%u", ir->value.u[i]); break; - case GLSL_TYPE_INT: printf("%d", ir->value.i[i]); break; - case GLSL_TYPE_FLOAT: printf("%f", ir->value.f[i]); break; - case GLSL_TYPE_BOOL: printf("%d", ir->value.b[i]); break; - default: assert(0); - } - } - } - printf(")) "); -} - - -void -ir_print_visitor::visit(ir_call *ir) -{ - printf("(call %s (", ir->callee_name()); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_instruction *const inst = (ir_instruction *) iter.get(); - - inst->accept(this); - } - printf("))\n"); -} - - -void -ir_print_visitor::visit(ir_return *ir) -{ - printf("(return"); - - ir_rvalue *const value = ir->get_value(); - if (value) { - printf(" "); - value->accept(this); - } - - printf(")"); -} - - -void -ir_print_visitor::visit(ir_discard *ir) -{ - printf("(discard "); - - if (ir->condition != NULL) { - printf(" "); - ir->condition->accept(this); - } - - printf(")"); -} - - -void -ir_print_visitor::visit(ir_if *ir) -{ - printf("(if "); - ir->condition->accept(this); - - printf("(\n"); - indentation++; - - foreach_iter(exec_list_iterator, iter, ir->then_instructions) { - ir_instruction *const inst = (ir_instruction *) iter.get(); - - indent(); - inst->accept(this); - printf("\n"); - } - - indentation--; - indent(); - printf(")\n"); - - indent(); - if (!ir->else_instructions.is_empty()) { - printf("(\n"); - indentation++; - - foreach_iter(exec_list_iterator, iter, ir->else_instructions) { - ir_instruction *const inst = (ir_instruction *) iter.get(); - - indent(); - inst->accept(this); - printf("\n"); - } - indentation--; - indent(); - printf("))\n"); - } else { - printf("())\n"); - } -} - - -void -ir_print_visitor::visit(ir_loop *ir) -{ - printf("(loop ("); - if (ir->counter != NULL) - ir->counter->accept(this); - printf(") ("); - if (ir->from != NULL) - ir->from->accept(this); - printf(") ("); - if (ir->to != NULL) - ir->to->accept(this); - printf(") ("); - if (ir->increment != NULL) - ir->increment->accept(this); - printf(") (\n"); - indentation++; - - foreach_iter(exec_list_iterator, iter, ir->body_instructions) { - ir_instruction *const inst = (ir_instruction *) iter.get(); - - indent(); - inst->accept(this); - printf("\n"); - } - indentation--; - indent(); - printf("))\n"); -} - - -void -ir_print_visitor::visit(ir_loop_jump *ir) -{ - printf("%s", ir->is_break() ? "break" : "continue"); -} +/* + * 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 "ir_print_visitor.h" +#include "glsl_types.h" +#include "glsl_parser_extras.h" + +extern "C" { +#include "program/hash_table.h" +} + +static void print_type(const glsl_type *t); + +void +ir_instruction::print(void) const +{ + ir_instruction *deconsted = const_cast(this); + + ir_print_visitor v; + deconsted->accept(&v); +} + +void +_mesa_print_ir(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + if (state) { + for (unsigned i = 0; i < state->num_user_structures; i++) { + const glsl_type *const s = state->user_structures[i]; + + printf("(structure (%s) (%s@%p) (%u) (\n", + s->name, s->name, (void *) s, s->length); + + for (unsigned j = 0; j < s->length; j++) { + printf("\t(("); + print_type(s->fields.structure[j].type); + printf(")(%s))\n", s->fields.structure[j].name); + } + + printf(")\n"); + } + } + + printf("(\n"); + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir->print(); + if (ir->ir_type != ir_type_function) + printf("\n"); + } + printf("\n)"); +} + +ir_print_visitor::ir_print_visitor() +{ + indentation = 0; + printable_names = + hash_table_ctor(32, hash_table_pointer_hash, hash_table_pointer_compare); + symbols = _mesa_symbol_table_ctor(); + mem_ctx = ralloc_context(NULL); +} + +ir_print_visitor::~ir_print_visitor() +{ + hash_table_dtor(printable_names); + _mesa_symbol_table_dtor(symbols); + ralloc_free(mem_ctx); +} + +void ir_print_visitor::indent(void) +{ + for (int i = 0; i < indentation; i++) + printf(" "); +} + +const char * +ir_print_visitor::unique_name(ir_variable *var) +{ + /* var->name can be NULL in function prototypes when a type is given for a + * parameter but no name is given. In that case, just return an empty + * string. Don't worry about tracking the generated name in the printable + * names hash because this is the only scope where it can ever appear. + */ + if (var->name == NULL) { + static unsigned arg = 1; + return ralloc_asprintf(this->mem_ctx, "parameter@%u", arg++); + } + + /* Do we already have a name for this variable? */ + const char *name = (const char *) hash_table_find(this->printable_names, var); + if (name != NULL) + return name; + + /* If there's no conflict, just use the original name */ + if (_mesa_symbol_table_find_symbol(this->symbols, -1, var->name) == NULL) { + name = var->name; + } else { + static unsigned i = 1; + name = ralloc_asprintf(this->mem_ctx, "%s@%u", var->name, ++i); + } + hash_table_insert(this->printable_names, (void *) name, var); + _mesa_symbol_table_add_symbol(this->symbols, -1, name, var); + return name; +} + +static void +print_type(const glsl_type *t) +{ + if (t->base_type == GLSL_TYPE_ARRAY) { + printf("(array "); + print_type(t->fields.array); + printf(" %u)", t->length); + } else if ((t->base_type == GLSL_TYPE_STRUCT) + && (strncmp("gl_", t->name, 3) != 0)) { + printf("%s@%p", t->name, (void *) t); + } else { + printf("%s", t->name); + } +} + + +void ir_print_visitor::visit(ir_variable *ir) +{ + printf("(declare "); + + const char *const cent = (ir->centroid) ? "centroid " : ""; + const char *const inv = (ir->invariant) ? "invariant " : ""; + const char *const mode[] = { "", "uniform ", "in ", "out ", "inout ", + "const_in ", "sys ", "temporary " }; + const char *const interp[] = { "", "flat", "noperspective" }; + + printf("(%s%s%s%s) ", + cent, inv, mode[ir->mode], interp[ir->interpolation]); + + print_type(ir->type); + printf(" %s)", unique_name(ir)); +} + + +void ir_print_visitor::visit(ir_function_signature *ir) +{ + _mesa_symbol_table_push_scope(symbols); + printf("(signature "); + indentation++; + + print_type(ir->return_type); + printf("\n"); + indent(); + + printf("(parameters\n"); + indentation++; + + foreach_iter(exec_list_iterator, iter, ir->parameters) { + ir_variable *const inst = (ir_variable *) iter.get(); + + indent(); + inst->accept(this); + printf("\n"); + } + indentation--; + + indent(); + printf(")\n"); + + indent(); + + printf("(\n"); + indentation++; + + foreach_iter(exec_list_iterator, iter, ir->body) { + ir_instruction *const inst = (ir_instruction *) iter.get(); + + indent(); + inst->accept(this); + printf("\n"); + } + indentation--; + indent(); + printf("))\n"); + indentation--; + _mesa_symbol_table_pop_scope(symbols); +} + + +void ir_print_visitor::visit(ir_function *ir) +{ + printf("(function %s\n", ir->name); + indentation++; + foreach_iter(exec_list_iterator, iter, *ir) { + ir_function_signature *const sig = (ir_function_signature *) iter.get(); + indent(); + sig->accept(this); + printf("\n"); + } + indentation--; + indent(); + printf(")\n\n"); +} + + +void ir_print_visitor::visit(ir_expression *ir) +{ + printf("(expression "); + + print_type(ir->type); + + printf(" %s ", ir->operator_string()); + + for (unsigned i = 0; i < ir->get_num_operands(); i++) { + ir->operands[i]->accept(this); + } + + printf(") "); +} + + +void ir_print_visitor::visit(ir_texture *ir) +{ + printf("(%s ", ir->opcode_string()); + + print_type(ir->type); + printf(" "); + + ir->sampler->accept(this); + printf(" "); + + if (ir->op != ir_txs) { + ir->coordinate->accept(this); + + printf(" "); + + if (ir->offset != NULL) { + ir->offset->accept(this); + } else { + printf("0"); + } + + printf(" "); + } + + if (ir->op != ir_txf && ir->op != ir_txs) { + if (ir->projector) + ir->projector->accept(this); + else + printf("1"); + + if (ir->shadow_comparitor) { + printf(" "); + ir->shadow_comparitor->accept(this); + } else { + printf(" ()"); + } + } + + printf(" "); + switch (ir->op) + { + case ir_tex: + break; + case ir_txb: + ir->lod_info.bias->accept(this); + break; + case ir_txl: + case ir_txf: + case ir_txs: + ir->lod_info.lod->accept(this); + break; + case ir_txd: + printf("("); + ir->lod_info.grad.dPdx->accept(this); + printf(" "); + ir->lod_info.grad.dPdy->accept(this); + printf(")"); + break; + }; + printf(")"); +} + + +void ir_print_visitor::visit(ir_swizzle *ir) +{ + const unsigned swiz[4] = { + ir->mask.x, + ir->mask.y, + ir->mask.z, + ir->mask.w, + }; + + printf("(swiz "); + for (unsigned i = 0; i < ir->mask.num_components; i++) { + printf("%c", "xyzw"[swiz[i]]); + } + printf(" "); + ir->val->accept(this); + printf(")"); +} + + +void ir_print_visitor::visit(ir_dereference_variable *ir) +{ + ir_variable *var = ir->variable_referenced(); + printf("(var_ref %s) ", unique_name(var)); +} + + +void ir_print_visitor::visit(ir_dereference_array *ir) +{ + printf("(array_ref "); + ir->array->accept(this); + ir->array_index->accept(this); + printf(") "); +} + + +void ir_print_visitor::visit(ir_dereference_record *ir) +{ + printf("(record_ref "); + ir->record->accept(this); + printf(" %s) ", ir->field); +} + + +void ir_print_visitor::visit(ir_assignment *ir) +{ + printf("(assign "); + + if (ir->condition) + ir->condition->accept(this); + + char mask[5]; + unsigned j = 0; + + for (unsigned i = 0; i < 4; i++) { + if ((ir->write_mask & (1 << i)) != 0) { + mask[j] = "xyzw"[i]; + j++; + } + } + mask[j] = '\0'; + + printf(" (%s) ", mask); + + ir->lhs->accept(this); + + printf(" "); + + ir->rhs->accept(this); + printf(") "); +} + + +void ir_print_visitor::visit(ir_constant *ir) +{ + const glsl_type *const base_type = ir->type->get_base_type(); + + printf("(constant "); + print_type(ir->type); + printf(" ("); + + if (ir->type->is_array()) { + for (unsigned i = 0; i < ir->type->length; i++) + ir->get_array_element(i)->accept(this); + } else if (ir->type->is_record()) { + ir_constant *value = (ir_constant *) ir->components.get_head(); + for (unsigned i = 0; i < ir->type->length; i++) { + printf("(%s ", ir->type->fields.structure[i].name); + value->accept(this); + printf(")"); + + value = (ir_constant *) value->next; + } + } else { + for (unsigned i = 0; i < ir->type->components(); i++) { + if (i != 0) + printf(" "); + switch (base_type->base_type) { + case GLSL_TYPE_UINT: printf("%u", ir->value.u[i]); break; + case GLSL_TYPE_INT: printf("%d", ir->value.i[i]); break; + case GLSL_TYPE_FLOAT: printf("%f", ir->value.f[i]); break; + case GLSL_TYPE_BOOL: printf("%d", ir->value.b[i]); break; + default: assert(0); + } + } + } + printf(")) "); +} + + +void +ir_print_visitor::visit(ir_call *ir) +{ + printf("(call %s (", ir->callee_name()); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_instruction *const inst = (ir_instruction *) iter.get(); + + inst->accept(this); + } + printf("))\n"); +} + + +void +ir_print_visitor::visit(ir_return *ir) +{ + printf("(return"); + + ir_rvalue *const value = ir->get_value(); + if (value) { + printf(" "); + value->accept(this); + } + + printf(")"); +} + + +void +ir_print_visitor::visit(ir_discard *ir) +{ + printf("(discard "); + + if (ir->condition != NULL) { + printf(" "); + ir->condition->accept(this); + } + + printf(")"); +} + + +void +ir_print_visitor::visit(ir_if *ir) +{ + printf("(if "); + ir->condition->accept(this); + + printf("(\n"); + indentation++; + + foreach_iter(exec_list_iterator, iter, ir->then_instructions) { + ir_instruction *const inst = (ir_instruction *) iter.get(); + + indent(); + inst->accept(this); + printf("\n"); + } + + indentation--; + indent(); + printf(")\n"); + + indent(); + if (!ir->else_instructions.is_empty()) { + printf("(\n"); + indentation++; + + foreach_iter(exec_list_iterator, iter, ir->else_instructions) { + ir_instruction *const inst = (ir_instruction *) iter.get(); + + indent(); + inst->accept(this); + printf("\n"); + } + indentation--; + indent(); + printf("))\n"); + } else { + printf("())\n"); + } +} + + +void +ir_print_visitor::visit(ir_loop *ir) +{ + printf("(loop ("); + if (ir->counter != NULL) + ir->counter->accept(this); + printf(") ("); + if (ir->from != NULL) + ir->from->accept(this); + printf(") ("); + if (ir->to != NULL) + ir->to->accept(this); + printf(") ("); + if (ir->increment != NULL) + ir->increment->accept(this); + printf(") (\n"); + indentation++; + + foreach_iter(exec_list_iterator, iter, ir->body_instructions) { + ir_instruction *const inst = (ir_instruction *) iter.get(); + + indent(); + inst->accept(this); + printf("\n"); + } + indentation--; + indent(); + printf("))\n"); +} + + +void +ir_print_visitor::visit(ir_loop_jump *ir) +{ + printf("%s", ir->is_break() ? "break" : "continue"); +} diff --git a/mesalib/src/glsl/ir_print_visitor.h b/mesalib/src/glsl/ir_print_visitor.h index ec4d00843..c7136f11a 100644 --- a/mesalib/src/glsl/ir_print_visitor.h +++ b/mesalib/src/glsl/ir_print_visitor.h @@ -1,94 +1,94 @@ -/* -*- c++ -*- */ -/* - * 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. - */ - -#pragma once -#ifndef IR_PRINT_VISITOR_H -#define IR_PRINT_VISITOR_H - -#include "ir.h" -#include "ir_visitor.h" - -extern "C" { -#include "program/symbol_table.h" -} - -extern void _mesa_print_ir(exec_list *instructions, - struct _mesa_glsl_parse_state *state); - -/** - * Abstract base class of visitors of IR instruction trees - */ -class ir_print_visitor : public ir_visitor { -public: - ir_print_visitor(); - virtual ~ir_print_visitor(); - - void indent(void); - - /** - * \name Visit methods - * - * As typical for the visitor pattern, there must be one \c visit method for - * each concrete subclass of \c ir_instruction. Virtual base classes within - * the hierarchy should not have \c visit methods. - */ - /*@{*/ - virtual void visit(ir_variable *); - virtual void visit(ir_function_signature *); - virtual void visit(ir_function *); - virtual void visit(ir_expression *); - virtual void visit(ir_texture *); - virtual void visit(ir_swizzle *); - virtual void visit(ir_dereference_variable *); - virtual void visit(ir_dereference_array *); - virtual void visit(ir_dereference_record *); - virtual void visit(ir_assignment *); - virtual void visit(ir_constant *); - virtual void visit(ir_call *); - virtual void visit(ir_return *); - virtual void visit(ir_discard *); - virtual void visit(ir_if *); - virtual void visit(ir_loop *); - virtual void visit(ir_loop_jump *); - /*@}*/ - -private: - /** - * Fetch/generate a unique name for ir_variable. - * - * GLSL IR permits multiple ir_variables to share the same name. This works - * fine until we try to print it, when we really need a unique one. - */ - const char *unique_name(ir_variable *var); - - /** A mapping from ir_variable * -> unique printable names. */ - hash_table *printable_names; - _mesa_symbol_table *symbols; - - void *mem_ctx; - - int indentation; -}; - -#endif /* IR_PRINT_VISITOR_H */ +/* -*- c++ -*- */ +/* + * 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. + */ + +#pragma once +#ifndef IR_PRINT_VISITOR_H +#define IR_PRINT_VISITOR_H + +#include "ir.h" +#include "ir_visitor.h" + +extern "C" { +#include "program/symbol_table.h" +} + +extern void _mesa_print_ir(exec_list *instructions, + struct _mesa_glsl_parse_state *state); + +/** + * Abstract base class of visitors of IR instruction trees + */ +class ir_print_visitor : public ir_visitor { +public: + ir_print_visitor(); + virtual ~ir_print_visitor(); + + void indent(void); + + /** + * \name Visit methods + * + * As typical for the visitor pattern, there must be one \c visit method for + * each concrete subclass of \c ir_instruction. Virtual base classes within + * the hierarchy should not have \c visit methods. + */ + /*@{*/ + virtual void visit(ir_variable *); + virtual void visit(ir_function_signature *); + virtual void visit(ir_function *); + virtual void visit(ir_expression *); + virtual void visit(ir_texture *); + virtual void visit(ir_swizzle *); + virtual void visit(ir_dereference_variable *); + virtual void visit(ir_dereference_array *); + virtual void visit(ir_dereference_record *); + virtual void visit(ir_assignment *); + virtual void visit(ir_constant *); + virtual void visit(ir_call *); + virtual void visit(ir_return *); + virtual void visit(ir_discard *); + virtual void visit(ir_if *); + virtual void visit(ir_loop *); + virtual void visit(ir_loop_jump *); + /*@}*/ + +private: + /** + * Fetch/generate a unique name for ir_variable. + * + * GLSL IR permits multiple ir_variables to share the same name. This works + * fine until we try to print it, when we really need a unique one. + */ + const char *unique_name(ir_variable *var); + + /** A mapping from ir_variable * -> unique printable names. */ + hash_table *printable_names; + _mesa_symbol_table *symbols; + + void *mem_ctx; + + int indentation; +}; + +#endif /* IR_PRINT_VISITOR_H */ diff --git a/mesalib/src/glsl/ir_validate.cpp b/mesalib/src/glsl/ir_validate.cpp index 90a4d60d2..2d1c6097c 100644 --- a/mesalib/src/glsl/ir_validate.cpp +++ b/mesalib/src/glsl/ir_validate.cpp @@ -1,622 +1,622 @@ -/* - * 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. - */ - -/** - * \file ir_validate.cpp - * - * Attempts to verify that various invariants of the IR tree are true. - * - * In particular, at the moment it makes sure that no single - * ir_instruction node except for ir_variable appears multiple times - * in the ir tree. ir_variable does appear multiple times: Once as a - * declaration in an exec_list, and multiple times as the endpoint of - * a dereference chain. - */ - -#include -#include "ir.h" -#include "ir_hierarchical_visitor.h" -#include "program/hash_table.h" -#include "glsl_types.h" - -class ir_validate : public ir_hierarchical_visitor { -public: - ir_validate() - { - this->ht = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - - this->current_function = NULL; - - this->callback = ir_validate::validate_ir; - this->data = ht; - } - - ~ir_validate() - { - hash_table_dtor(this->ht); - } - - virtual ir_visitor_status visit(ir_variable *v); - virtual ir_visitor_status visit(ir_dereference_variable *ir); - - virtual ir_visitor_status visit_enter(ir_if *ir); - - virtual ir_visitor_status visit_leave(ir_loop *ir); - virtual ir_visitor_status visit_enter(ir_function *ir); - virtual ir_visitor_status visit_leave(ir_function *ir); - virtual ir_visitor_status visit_enter(ir_function_signature *ir); - - virtual ir_visitor_status visit_leave(ir_expression *ir); - virtual ir_visitor_status visit_leave(ir_swizzle *ir); - - virtual ir_visitor_status visit_enter(ir_assignment *ir); - virtual ir_visitor_status visit_enter(ir_call *ir); - - static void validate_ir(ir_instruction *ir, void *data); - - ir_function *current_function; - - struct hash_table *ht; -}; - - -ir_visitor_status -ir_validate::visit(ir_dereference_variable *ir) -{ - if ((ir->var == NULL) || (ir->var->as_variable() == NULL)) { - printf("ir_dereference_variable @ %p does not specify a variable %p\n", - (void *) ir, (void *) ir->var); - abort(); - } - - if (hash_table_find(ht, ir->var) == NULL) { - printf("ir_dereference_variable @ %p specifies undeclared variable " - "`%s' @ %p\n", - (void *) ir, ir->var->name, (void *) ir->var); - abort(); - } - - this->validate_ir(ir, this->data); - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_enter(ir_if *ir) -{ - if (ir->condition->type != glsl_type::bool_type) { - printf("ir_if condition %s type instead of bool.\n", - ir->condition->type->name); - ir->print(); - printf("\n"); - abort(); - } - - return visit_continue; -} - - -ir_visitor_status -ir_validate::visit_leave(ir_loop *ir) -{ - if (ir->counter != NULL) { - if ((ir->from == NULL) || (ir->from == NULL) || (ir->increment == NULL)) { - printf("ir_loop has invalid loop controls:\n" - " counter: %p\n" - " from: %p\n" - " to: %p\n" - " increment: %p\n", - (void *) ir->counter, (void *) ir->from, (void *) ir->to, - (void *) ir->increment); - abort(); - } - - if ((ir->cmp < ir_binop_less) || (ir->cmp > ir_binop_nequal)) { - printf("ir_loop has invalid comparitor %d\n", ir->cmp); - abort(); - } - } else { - if ((ir->from != NULL) || (ir->from != NULL) || (ir->increment != NULL)) { - printf("ir_loop has invalid loop controls:\n" - " counter: %p\n" - " from: %p\n" - " to: %p\n" - " increment: %p\n", - (void *) ir->counter, (void *) ir->from, (void *) ir->to, - (void *) ir->increment); - abort(); - } - } - - return visit_continue; -} - - -ir_visitor_status -ir_validate::visit_enter(ir_function *ir) -{ - /* Function definitions cannot be nested. - */ - if (this->current_function != NULL) { - printf("Function definition nested inside another function " - "definition:\n"); - printf("%s %p inside %s %p\n", - ir->name, (void *) ir, - this->current_function->name, (void *) this->current_function); - abort(); - } - - /* Store the current function hierarchy being traversed. This is used - * by the function signature visitor to ensure that the signatures are - * linked with the correct functions. - */ - this->current_function = ir; - - this->validate_ir(ir, this->data); - - /* Verify that all of the things stored in the list of signatures are, - * in fact, function signatures. - */ - foreach_list(node, &ir->signatures) { - ir_instruction *sig = (ir_instruction *) node; - - if (sig->ir_type != ir_type_function_signature) { - printf("Non-signature in signature list of function `%s'\n", - ir->name); - abort(); - } - } - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_leave(ir_function *ir) -{ - assert(ralloc_parent(ir->name) == ir); - - this->current_function = NULL; - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_enter(ir_function_signature *ir) -{ - if (this->current_function != ir->function()) { - printf("Function signature nested inside wrong function " - "definition:\n"); - printf("%p inside %s %p instead of %s %p\n", - (void *) ir, - this->current_function->name, (void *) this->current_function, - ir->function_name(), (void *) ir->function()); - abort(); - } - - if (ir->return_type == NULL) { - printf("Function signature %p for function %s has NULL return type.\n", - (void *) ir, ir->function_name()); - abort(); - } - - this->validate_ir(ir, this->data); - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_leave(ir_expression *ir) -{ - switch (ir->operation) { - case ir_unop_bit_not: - assert(ir->operands[0]->type == ir->type); - break; - case ir_unop_logic_not: - assert(ir->type->base_type == GLSL_TYPE_BOOL); - assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); - break; - - case ir_unop_neg: - case ir_unop_abs: - case ir_unop_sign: - case ir_unop_rcp: - case ir_unop_rsq: - case ir_unop_sqrt: - assert(ir->type == ir->operands[0]->type); - break; - - case ir_unop_exp: - case ir_unop_log: - case ir_unop_exp2: - case ir_unop_log2: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); - assert(ir->type == ir->operands[0]->type); - break; - - case ir_unop_f2i: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); - assert(ir->type->base_type == GLSL_TYPE_INT); - break; - case ir_unop_i2f: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); - assert(ir->type->base_type == GLSL_TYPE_FLOAT); - break; - case ir_unop_f2b: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); - assert(ir->type->base_type == GLSL_TYPE_BOOL); - break; - case ir_unop_b2f: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); - assert(ir->type->base_type == GLSL_TYPE_FLOAT); - break; - case ir_unop_i2b: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); - assert(ir->type->base_type == GLSL_TYPE_BOOL); - break; - case ir_unop_b2i: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); - assert(ir->type->base_type == GLSL_TYPE_INT); - break; - case ir_unop_u2f: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_UINT); - assert(ir->type->base_type == GLSL_TYPE_FLOAT); - break; - case ir_unop_i2u: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); - assert(ir->type->base_type == GLSL_TYPE_UINT); - break; - case ir_unop_u2i: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_UINT); - assert(ir->type->base_type == GLSL_TYPE_INT); - break; - - case ir_unop_any: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); - assert(ir->type == glsl_type::bool_type); - break; - - case ir_unop_trunc: - case ir_unop_round_even: - case ir_unop_ceil: - case ir_unop_floor: - case ir_unop_fract: - case ir_unop_sin: - case ir_unop_cos: - case ir_unop_sin_reduced: - case ir_unop_cos_reduced: - case ir_unop_dFdx: - case ir_unop_dFdy: - assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); - assert(ir->operands[0]->type == ir->type); - break; - - case ir_unop_noise: - /* XXX what can we assert here? */ - break; - - case ir_binop_add: - case ir_binop_sub: - case ir_binop_mul: - case ir_binop_div: - case ir_binop_mod: - case ir_binop_min: - case ir_binop_max: - case ir_binop_pow: - if (ir->operands[0]->type->is_scalar()) - assert(ir->operands[1]->type == ir->type); - else if (ir->operands[1]->type->is_scalar()) - assert(ir->operands[0]->type == ir->type); - else if (ir->operands[0]->type->is_vector() && - ir->operands[1]->type->is_vector()) { - assert(ir->operands[0]->type == ir->operands[1]->type); - assert(ir->operands[0]->type == ir->type); - } - break; - - case ir_binop_less: - case ir_binop_greater: - case ir_binop_lequal: - case ir_binop_gequal: - case ir_binop_equal: - case ir_binop_nequal: - /* The semantics of the IR operators differ from the GLSL <, >, <=, >=, - * ==, and != operators. The IR operators perform a component-wise - * comparison on scalar or vector types and return a boolean scalar or - * vector type of the same size. - */ - assert(ir->type->base_type == GLSL_TYPE_BOOL); - assert(ir->operands[0]->type == ir->operands[1]->type); - assert(ir->operands[0]->type->is_vector() - || ir->operands[0]->type->is_scalar()); - assert(ir->operands[0]->type->vector_elements - == ir->type->vector_elements); - break; - - case ir_binop_all_equal: - case ir_binop_any_nequal: - /* GLSL == and != operate on scalars, vectors, matrices and arrays, and - * return a scalar boolean. The IR matches that. - */ - assert(ir->type == glsl_type::bool_type); - assert(ir->operands[0]->type == ir->operands[1]->type); - break; - - case ir_binop_lshift: - case ir_binop_rshift: - assert(ir->operands[0]->type->is_integer() && - ir->operands[1]->type->is_integer()); - if (ir->operands[0]->type->is_scalar()) { - assert(ir->operands[1]->type->is_scalar()); - } - if (ir->operands[0]->type->is_vector() && - ir->operands[1]->type->is_vector()) { - assert(ir->operands[0]->type->components() == - ir->operands[1]->type->components()); - } - assert(ir->type == ir->operands[0]->type); - break; - - case ir_binop_bit_and: - case ir_binop_bit_xor: - case ir_binop_bit_or: - assert(ir->operands[0]->type->base_type == - ir->operands[1]->type->base_type); - assert(ir->type->is_integer()); - if (ir->operands[0]->type->is_vector() && - ir->operands[1]->type->is_vector()) { - assert(ir->operands[0]->type->vector_elements == - ir->operands[1]->type->vector_elements); - } - break; - - case ir_binop_logic_and: - case ir_binop_logic_xor: - case ir_binop_logic_or: - assert(ir->type == glsl_type::bool_type); - assert(ir->operands[0]->type == glsl_type::bool_type); - assert(ir->operands[1]->type == glsl_type::bool_type); - break; - - case ir_binop_dot: - assert(ir->type == glsl_type::float_type); - assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); - assert(ir->operands[0]->type->is_vector()); - assert(ir->operands[0]->type == ir->operands[1]->type); - break; - - case ir_quadop_vector: - /* The vector operator collects some number of scalars and generates a - * vector from them. - * - * - All of the operands must be scalar. - * - Number of operands must matche the size of the resulting vector. - * - Base type of the operands must match the base type of the result. - */ - assert(ir->type->is_vector()); - switch (ir->type->vector_elements) { - case 2: - assert(ir->operands[0]->type->is_scalar()); - assert(ir->operands[0]->type->base_type == ir->type->base_type); - assert(ir->operands[1]->type->is_scalar()); - assert(ir->operands[1]->type->base_type == ir->type->base_type); - assert(ir->operands[2] == NULL); - assert(ir->operands[3] == NULL); - break; - case 3: - assert(ir->operands[0]->type->is_scalar()); - assert(ir->operands[0]->type->base_type == ir->type->base_type); - assert(ir->operands[1]->type->is_scalar()); - assert(ir->operands[1]->type->base_type == ir->type->base_type); - assert(ir->operands[2]->type->is_scalar()); - assert(ir->operands[2]->type->base_type == ir->type->base_type); - assert(ir->operands[3] == NULL); - break; - case 4: - assert(ir->operands[0]->type->is_scalar()); - assert(ir->operands[0]->type->base_type == ir->type->base_type); - assert(ir->operands[1]->type->is_scalar()); - assert(ir->operands[1]->type->base_type == ir->type->base_type); - assert(ir->operands[2]->type->is_scalar()); - assert(ir->operands[2]->type->base_type == ir->type->base_type); - assert(ir->operands[3]->type->is_scalar()); - assert(ir->operands[3]->type->base_type == ir->type->base_type); - break; - default: - /* The is_vector assertion above should prevent execution from ever - * getting here. - */ - assert(!"Should not get here."); - break; - } - } - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_leave(ir_swizzle *ir) -{ - int chans[4] = {ir->mask.x, ir->mask.y, ir->mask.z, ir->mask.w}; - - for (unsigned int i = 0; i < ir->type->vector_elements; i++) { - if (chans[i] >= ir->val->type->vector_elements) { - printf("ir_swizzle @ %p specifies a channel not present " - "in the value.\n", (void *) ir); - ir->print(); - abort(); - } - } - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit(ir_variable *ir) -{ - /* An ir_variable is the one thing that can (and will) appear multiple times - * in an IR tree. It is added to the hashtable so that it can be used - * in the ir_dereference_variable handler to ensure that a variable is - * declared before it is dereferenced. - */ - if (ir->name) - assert(ralloc_parent(ir->name) == ir); - - hash_table_insert(ht, ir, ir); - - - /* If a variable is an array, verify that the maximum array index is in - * bounds. There was once an error in AST-to-HIR conversion that set this - * to be out of bounds. - */ - if (ir->type->array_size() > 0) { - if (ir->max_array_access >= ir->type->length) { - printf("ir_variable has maximum access out of bounds (%d vs %d)\n", - ir->max_array_access, ir->type->length - 1); - ir->print(); - abort(); - } - } - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_enter(ir_assignment *ir) -{ - const ir_dereference *const lhs = ir->lhs; - if (lhs->type->is_scalar() || lhs->type->is_vector()) { - if (ir->write_mask == 0) { - printf("Assignment LHS is %s, but write mask is 0:\n", - lhs->type->is_scalar() ? "scalar" : "vector"); - ir->print(); - abort(); - } - - int lhs_components = 0; - for (int i = 0; i < 4; i++) { - if (ir->write_mask & (1 << i)) - lhs_components++; - } - - if (lhs_components != ir->rhs->type->vector_elements) { - printf("Assignment count of LHS write mask channels enabled not\n" - "matching RHS vector size (%d LHS, %d RHS).\n", - lhs_components, ir->rhs->type->vector_elements); - ir->print(); - abort(); - } - } - - this->validate_ir(ir, this->data); - - return visit_continue; -} - -ir_visitor_status -ir_validate::visit_enter(ir_call *ir) -{ - ir_function_signature *const callee = ir->get_callee(); - - if (callee->ir_type != ir_type_function_signature) { - printf("IR called by ir_call is not ir_function_signature!\n"); - abort(); - } - - const exec_node *formal_param_node = callee->parameters.head; - const exec_node *actual_param_node = ir->actual_parameters.head; - while (true) { - if (formal_param_node->is_tail_sentinel() - != actual_param_node->is_tail_sentinel()) { - printf("ir_call has the wrong number of parameters:\n"); - goto dump_ir; - } - if (formal_param_node->is_tail_sentinel()) { - break; - } - const ir_variable *formal_param - = (const ir_variable *) formal_param_node; - const ir_rvalue *actual_param - = (const ir_rvalue *) actual_param_node; - if (formal_param->type != actual_param->type) { - printf("ir_call parameter type mismatch:\n"); - goto dump_ir; - } - if (formal_param->mode == ir_var_out - || formal_param->mode == ir_var_inout) { - if (!actual_param->is_lvalue()) { - printf("ir_call out/inout parameters must be lvalues:\n"); - goto dump_ir; - } - } - formal_param_node = formal_param_node->next; - actual_param_node = actual_param_node->next; - } - - return visit_continue; - -dump_ir: - ir->print(); - printf("callee:\n"); - callee->print(); - abort(); -} - -void -ir_validate::validate_ir(ir_instruction *ir, void *data) -{ - struct hash_table *ht = (struct hash_table *) data; - - if (hash_table_find(ht, ir)) { - printf("Instruction node present twice in ir tree:\n"); - ir->print(); - printf("\n"); - abort(); - } - hash_table_insert(ht, ir, ir); -} - -void -check_node_type(ir_instruction *ir, void *data) -{ - (void) data; - - if (ir->ir_type <= ir_type_unset || ir->ir_type >= ir_type_max) { - printf("Instruction node with unset type\n"); - ir->print(); printf("\n"); - } - assert(ir->type != glsl_type::error_type); -} - -void -validate_ir_tree(exec_list *instructions) -{ - ir_validate v; - - v.run(instructions); - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - - visit_tree(ir, check_node_type, NULL); - } -} +/* + * 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. + */ + +/** + * \file ir_validate.cpp + * + * Attempts to verify that various invariants of the IR tree are true. + * + * In particular, at the moment it makes sure that no single + * ir_instruction node except for ir_variable appears multiple times + * in the ir tree. ir_variable does appear multiple times: Once as a + * declaration in an exec_list, and multiple times as the endpoint of + * a dereference chain. + */ + +#include +#include "ir.h" +#include "ir_hierarchical_visitor.h" +#include "program/hash_table.h" +#include "glsl_types.h" + +class ir_validate : public ir_hierarchical_visitor { +public: + ir_validate() + { + this->ht = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + + this->current_function = NULL; + + this->callback = ir_validate::validate_ir; + this->data = ht; + } + + ~ir_validate() + { + hash_table_dtor(this->ht); + } + + virtual ir_visitor_status visit(ir_variable *v); + virtual ir_visitor_status visit(ir_dereference_variable *ir); + + virtual ir_visitor_status visit_enter(ir_if *ir); + + virtual ir_visitor_status visit_leave(ir_loop *ir); + virtual ir_visitor_status visit_enter(ir_function *ir); + virtual ir_visitor_status visit_leave(ir_function *ir); + virtual ir_visitor_status visit_enter(ir_function_signature *ir); + + virtual ir_visitor_status visit_leave(ir_expression *ir); + virtual ir_visitor_status visit_leave(ir_swizzle *ir); + + virtual ir_visitor_status visit_enter(ir_assignment *ir); + virtual ir_visitor_status visit_enter(ir_call *ir); + + static void validate_ir(ir_instruction *ir, void *data); + + ir_function *current_function; + + struct hash_table *ht; +}; + + +ir_visitor_status +ir_validate::visit(ir_dereference_variable *ir) +{ + if ((ir->var == NULL) || (ir->var->as_variable() == NULL)) { + printf("ir_dereference_variable @ %p does not specify a variable %p\n", + (void *) ir, (void *) ir->var); + abort(); + } + + if (hash_table_find(ht, ir->var) == NULL) { + printf("ir_dereference_variable @ %p specifies undeclared variable " + "`%s' @ %p\n", + (void *) ir, ir->var->name, (void *) ir->var); + abort(); + } + + this->validate_ir(ir, this->data); + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_enter(ir_if *ir) +{ + if (ir->condition->type != glsl_type::bool_type) { + printf("ir_if condition %s type instead of bool.\n", + ir->condition->type->name); + ir->print(); + printf("\n"); + abort(); + } + + return visit_continue; +} + + +ir_visitor_status +ir_validate::visit_leave(ir_loop *ir) +{ + if (ir->counter != NULL) { + if ((ir->from == NULL) || (ir->from == NULL) || (ir->increment == NULL)) { + printf("ir_loop has invalid loop controls:\n" + " counter: %p\n" + " from: %p\n" + " to: %p\n" + " increment: %p\n", + (void *) ir->counter, (void *) ir->from, (void *) ir->to, + (void *) ir->increment); + abort(); + } + + if ((ir->cmp < ir_binop_less) || (ir->cmp > ir_binop_nequal)) { + printf("ir_loop has invalid comparitor %d\n", ir->cmp); + abort(); + } + } else { + if ((ir->from != NULL) || (ir->from != NULL) || (ir->increment != NULL)) { + printf("ir_loop has invalid loop controls:\n" + " counter: %p\n" + " from: %p\n" + " to: %p\n" + " increment: %p\n", + (void *) ir->counter, (void *) ir->from, (void *) ir->to, + (void *) ir->increment); + abort(); + } + } + + return visit_continue; +} + + +ir_visitor_status +ir_validate::visit_enter(ir_function *ir) +{ + /* Function definitions cannot be nested. + */ + if (this->current_function != NULL) { + printf("Function definition nested inside another function " + "definition:\n"); + printf("%s %p inside %s %p\n", + ir->name, (void *) ir, + this->current_function->name, (void *) this->current_function); + abort(); + } + + /* Store the current function hierarchy being traversed. This is used + * by the function signature visitor to ensure that the signatures are + * linked with the correct functions. + */ + this->current_function = ir; + + this->validate_ir(ir, this->data); + + /* Verify that all of the things stored in the list of signatures are, + * in fact, function signatures. + */ + foreach_list(node, &ir->signatures) { + ir_instruction *sig = (ir_instruction *) node; + + if (sig->ir_type != ir_type_function_signature) { + printf("Non-signature in signature list of function `%s'\n", + ir->name); + abort(); + } + } + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_leave(ir_function *ir) +{ + assert(ralloc_parent(ir->name) == ir); + + this->current_function = NULL; + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_enter(ir_function_signature *ir) +{ + if (this->current_function != ir->function()) { + printf("Function signature nested inside wrong function " + "definition:\n"); + printf("%p inside %s %p instead of %s %p\n", + (void *) ir, + this->current_function->name, (void *) this->current_function, + ir->function_name(), (void *) ir->function()); + abort(); + } + + if (ir->return_type == NULL) { + printf("Function signature %p for function %s has NULL return type.\n", + (void *) ir, ir->function_name()); + abort(); + } + + this->validate_ir(ir, this->data); + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_leave(ir_expression *ir) +{ + switch (ir->operation) { + case ir_unop_bit_not: + assert(ir->operands[0]->type == ir->type); + break; + case ir_unop_logic_not: + assert(ir->type->base_type == GLSL_TYPE_BOOL); + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + break; + + case ir_unop_neg: + case ir_unop_abs: + case ir_unop_sign: + case ir_unop_rcp: + case ir_unop_rsq: + case ir_unop_sqrt: + assert(ir->type == ir->operands[0]->type); + break; + + case ir_unop_exp: + case ir_unop_log: + case ir_unop_exp2: + case ir_unop_log2: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + assert(ir->type == ir->operands[0]->type); + break; + + case ir_unop_f2i: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + assert(ir->type->base_type == GLSL_TYPE_INT); + break; + case ir_unop_i2f: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); + assert(ir->type->base_type == GLSL_TYPE_FLOAT); + break; + case ir_unop_f2b: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + assert(ir->type->base_type == GLSL_TYPE_BOOL); + break; + case ir_unop_b2f: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + assert(ir->type->base_type == GLSL_TYPE_FLOAT); + break; + case ir_unop_i2b: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); + assert(ir->type->base_type == GLSL_TYPE_BOOL); + break; + case ir_unop_b2i: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + assert(ir->type->base_type == GLSL_TYPE_INT); + break; + case ir_unop_u2f: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_UINT); + assert(ir->type->base_type == GLSL_TYPE_FLOAT); + break; + case ir_unop_i2u: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_INT); + assert(ir->type->base_type == GLSL_TYPE_UINT); + break; + case ir_unop_u2i: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_UINT); + assert(ir->type->base_type == GLSL_TYPE_INT); + break; + + case ir_unop_any: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_BOOL); + assert(ir->type == glsl_type::bool_type); + break; + + case ir_unop_trunc: + case ir_unop_round_even: + case ir_unop_ceil: + case ir_unop_floor: + case ir_unop_fract: + case ir_unop_sin: + case ir_unop_cos: + case ir_unop_sin_reduced: + case ir_unop_cos_reduced: + case ir_unop_dFdx: + case ir_unop_dFdy: + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + assert(ir->operands[0]->type == ir->type); + break; + + case ir_unop_noise: + /* XXX what can we assert here? */ + break; + + case ir_binop_add: + case ir_binop_sub: + case ir_binop_mul: + case ir_binop_div: + case ir_binop_mod: + case ir_binop_min: + case ir_binop_max: + case ir_binop_pow: + if (ir->operands[0]->type->is_scalar()) + assert(ir->operands[1]->type == ir->type); + else if (ir->operands[1]->type->is_scalar()) + assert(ir->operands[0]->type == ir->type); + else if (ir->operands[0]->type->is_vector() && + ir->operands[1]->type->is_vector()) { + assert(ir->operands[0]->type == ir->operands[1]->type); + assert(ir->operands[0]->type == ir->type); + } + break; + + case ir_binop_less: + case ir_binop_greater: + case ir_binop_lequal: + case ir_binop_gequal: + case ir_binop_equal: + case ir_binop_nequal: + /* The semantics of the IR operators differ from the GLSL <, >, <=, >=, + * ==, and != operators. The IR operators perform a component-wise + * comparison on scalar or vector types and return a boolean scalar or + * vector type of the same size. + */ + assert(ir->type->base_type == GLSL_TYPE_BOOL); + assert(ir->operands[0]->type == ir->operands[1]->type); + assert(ir->operands[0]->type->is_vector() + || ir->operands[0]->type->is_scalar()); + assert(ir->operands[0]->type->vector_elements + == ir->type->vector_elements); + break; + + case ir_binop_all_equal: + case ir_binop_any_nequal: + /* GLSL == and != operate on scalars, vectors, matrices and arrays, and + * return a scalar boolean. The IR matches that. + */ + assert(ir->type == glsl_type::bool_type); + assert(ir->operands[0]->type == ir->operands[1]->type); + break; + + case ir_binop_lshift: + case ir_binop_rshift: + assert(ir->operands[0]->type->is_integer() && + ir->operands[1]->type->is_integer()); + if (ir->operands[0]->type->is_scalar()) { + assert(ir->operands[1]->type->is_scalar()); + } + if (ir->operands[0]->type->is_vector() && + ir->operands[1]->type->is_vector()) { + assert(ir->operands[0]->type->components() == + ir->operands[1]->type->components()); + } + assert(ir->type == ir->operands[0]->type); + break; + + case ir_binop_bit_and: + case ir_binop_bit_xor: + case ir_binop_bit_or: + assert(ir->operands[0]->type->base_type == + ir->operands[1]->type->base_type); + assert(ir->type->is_integer()); + if (ir->operands[0]->type->is_vector() && + ir->operands[1]->type->is_vector()) { + assert(ir->operands[0]->type->vector_elements == + ir->operands[1]->type->vector_elements); + } + break; + + case ir_binop_logic_and: + case ir_binop_logic_xor: + case ir_binop_logic_or: + assert(ir->type == glsl_type::bool_type); + assert(ir->operands[0]->type == glsl_type::bool_type); + assert(ir->operands[1]->type == glsl_type::bool_type); + break; + + case ir_binop_dot: + assert(ir->type == glsl_type::float_type); + assert(ir->operands[0]->type->base_type == GLSL_TYPE_FLOAT); + assert(ir->operands[0]->type->is_vector()); + assert(ir->operands[0]->type == ir->operands[1]->type); + break; + + case ir_quadop_vector: + /* The vector operator collects some number of scalars and generates a + * vector from them. + * + * - All of the operands must be scalar. + * - Number of operands must matche the size of the resulting vector. + * - Base type of the operands must match the base type of the result. + */ + assert(ir->type->is_vector()); + switch (ir->type->vector_elements) { + case 2: + assert(ir->operands[0]->type->is_scalar()); + assert(ir->operands[0]->type->base_type == ir->type->base_type); + assert(ir->operands[1]->type->is_scalar()); + assert(ir->operands[1]->type->base_type == ir->type->base_type); + assert(ir->operands[2] == NULL); + assert(ir->operands[3] == NULL); + break; + case 3: + assert(ir->operands[0]->type->is_scalar()); + assert(ir->operands[0]->type->base_type == ir->type->base_type); + assert(ir->operands[1]->type->is_scalar()); + assert(ir->operands[1]->type->base_type == ir->type->base_type); + assert(ir->operands[2]->type->is_scalar()); + assert(ir->operands[2]->type->base_type == ir->type->base_type); + assert(ir->operands[3] == NULL); + break; + case 4: + assert(ir->operands[0]->type->is_scalar()); + assert(ir->operands[0]->type->base_type == ir->type->base_type); + assert(ir->operands[1]->type->is_scalar()); + assert(ir->operands[1]->type->base_type == ir->type->base_type); + assert(ir->operands[2]->type->is_scalar()); + assert(ir->operands[2]->type->base_type == ir->type->base_type); + assert(ir->operands[3]->type->is_scalar()); + assert(ir->operands[3]->type->base_type == ir->type->base_type); + break; + default: + /* The is_vector assertion above should prevent execution from ever + * getting here. + */ + assert(!"Should not get here."); + break; + } + } + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_leave(ir_swizzle *ir) +{ + int chans[4] = {ir->mask.x, ir->mask.y, ir->mask.z, ir->mask.w}; + + for (unsigned int i = 0; i < ir->type->vector_elements; i++) { + if (chans[i] >= ir->val->type->vector_elements) { + printf("ir_swizzle @ %p specifies a channel not present " + "in the value.\n", (void *) ir); + ir->print(); + abort(); + } + } + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit(ir_variable *ir) +{ + /* An ir_variable is the one thing that can (and will) appear multiple times + * in an IR tree. It is added to the hashtable so that it can be used + * in the ir_dereference_variable handler to ensure that a variable is + * declared before it is dereferenced. + */ + if (ir->name) + assert(ralloc_parent(ir->name) == ir); + + hash_table_insert(ht, ir, ir); + + + /* If a variable is an array, verify that the maximum array index is in + * bounds. There was once an error in AST-to-HIR conversion that set this + * to be out of bounds. + */ + if (ir->type->array_size() > 0) { + if (ir->max_array_access >= ir->type->length) { + printf("ir_variable has maximum access out of bounds (%d vs %d)\n", + ir->max_array_access, ir->type->length - 1); + ir->print(); + abort(); + } + } + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_enter(ir_assignment *ir) +{ + const ir_dereference *const lhs = ir->lhs; + if (lhs->type->is_scalar() || lhs->type->is_vector()) { + if (ir->write_mask == 0) { + printf("Assignment LHS is %s, but write mask is 0:\n", + lhs->type->is_scalar() ? "scalar" : "vector"); + ir->print(); + abort(); + } + + int lhs_components = 0; + for (int i = 0; i < 4; i++) { + if (ir->write_mask & (1 << i)) + lhs_components++; + } + + if (lhs_components != ir->rhs->type->vector_elements) { + printf("Assignment count of LHS write mask channels enabled not\n" + "matching RHS vector size (%d LHS, %d RHS).\n", + lhs_components, ir->rhs->type->vector_elements); + ir->print(); + abort(); + } + } + + this->validate_ir(ir, this->data); + + return visit_continue; +} + +ir_visitor_status +ir_validate::visit_enter(ir_call *ir) +{ + ir_function_signature *const callee = ir->get_callee(); + + if (callee->ir_type != ir_type_function_signature) { + printf("IR called by ir_call is not ir_function_signature!\n"); + abort(); + } + + const exec_node *formal_param_node = callee->parameters.head; + const exec_node *actual_param_node = ir->actual_parameters.head; + while (true) { + if (formal_param_node->is_tail_sentinel() + != actual_param_node->is_tail_sentinel()) { + printf("ir_call has the wrong number of parameters:\n"); + goto dump_ir; + } + if (formal_param_node->is_tail_sentinel()) { + break; + } + const ir_variable *formal_param + = (const ir_variable *) formal_param_node; + const ir_rvalue *actual_param + = (const ir_rvalue *) actual_param_node; + if (formal_param->type != actual_param->type) { + printf("ir_call parameter type mismatch:\n"); + goto dump_ir; + } + if (formal_param->mode == ir_var_out + || formal_param->mode == ir_var_inout) { + if (!actual_param->is_lvalue()) { + printf("ir_call out/inout parameters must be lvalues:\n"); + goto dump_ir; + } + } + formal_param_node = formal_param_node->next; + actual_param_node = actual_param_node->next; + } + + return visit_continue; + +dump_ir: + ir->print(); + printf("callee:\n"); + callee->print(); + abort(); +} + +void +ir_validate::validate_ir(ir_instruction *ir, void *data) +{ + struct hash_table *ht = (struct hash_table *) data; + + if (hash_table_find(ht, ir)) { + printf("Instruction node present twice in ir tree:\n"); + ir->print(); + printf("\n"); + abort(); + } + hash_table_insert(ht, ir, ir); +} + +void +check_node_type(ir_instruction *ir, void *data) +{ + (void) data; + + if (ir->ir_type <= ir_type_unset || ir->ir_type >= ir_type_max) { + printf("Instruction node with unset type\n"); + ir->print(); printf("\n"); + } + assert(ir->type != glsl_type::error_type); +} + +void +validate_ir_tree(exec_list *instructions) +{ + ir_validate v; + + v.run(instructions); + + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + + visit_tree(ir, check_node_type, NULL); + } +} diff --git a/mesalib/src/glsl/ir_variable.cpp b/mesalib/src/glsl/ir_variable.cpp index 76f79da92..e0b6f38f1 100644 --- a/mesalib/src/glsl/ir_variable.cpp +++ b/mesalib/src/glsl/ir_variable.cpp @@ -1,892 +1,892 @@ -/* - * 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 "ir.h" -#include "glsl_parser_extras.h" -#include "glsl_symbol_table.h" -#include "builtin_variables.h" -#include "main/uniforms.h" -#include "program/prog_parameter.h" -#include "program/prog_statevars.h" -#include "program/prog_instruction.h" - -static void generate_ARB_draw_buffers_variables(exec_list *, - struct _mesa_glsl_parse_state *, - bool, _mesa_glsl_parser_targets); - -static void -generate_ARB_draw_instanced_variables(exec_list *, - struct _mesa_glsl_parse_state *, - bool, _mesa_glsl_parser_targets); - -static struct gl_builtin_uniform_element gl_DepthRange_elements[] = { - {"near", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_XXXX}, - {"far", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_YYYY}, - {"diff", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_ZZZZ}, -}; - -static struct gl_builtin_uniform_element gl_ClipPlane_elements[] = { - {NULL, {STATE_CLIPPLANE, 0, 0}, SWIZZLE_XYZW} -}; - -static struct gl_builtin_uniform_element gl_Point_elements[] = { - {"size", {STATE_POINT_SIZE}, SWIZZLE_XXXX}, - {"sizeMin", {STATE_POINT_SIZE}, SWIZZLE_YYYY}, - {"sizeMax", {STATE_POINT_SIZE}, SWIZZLE_ZZZZ}, - {"fadeThresholdSize", {STATE_POINT_SIZE}, SWIZZLE_WWWW}, - {"distanceConstantAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_XXXX}, - {"distanceLinearAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_YYYY}, - {"distanceQuadraticAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_ZZZZ}, -}; - -static struct gl_builtin_uniform_element gl_FrontMaterial_elements[] = { - {"emission", {STATE_MATERIAL, 0, STATE_EMISSION}, SWIZZLE_XYZW}, - {"ambient", {STATE_MATERIAL, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"diffuse", {STATE_MATERIAL, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"specular", {STATE_MATERIAL, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, - {"shininess", {STATE_MATERIAL, 0, STATE_SHININESS}, SWIZZLE_XXXX}, -}; - -static struct gl_builtin_uniform_element gl_BackMaterial_elements[] = { - {"emission", {STATE_MATERIAL, 1, STATE_EMISSION}, SWIZZLE_XYZW}, - {"ambient", {STATE_MATERIAL, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"diffuse", {STATE_MATERIAL, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"specular", {STATE_MATERIAL, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, - {"shininess", {STATE_MATERIAL, 1, STATE_SHININESS}, SWIZZLE_XXXX}, -}; - -static struct gl_builtin_uniform_element gl_LightSource_elements[] = { - {"ambient", {STATE_LIGHT, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"diffuse", {STATE_LIGHT, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"specular", {STATE_LIGHT, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, - {"position", {STATE_LIGHT, 0, STATE_POSITION}, SWIZZLE_XYZW}, - {"halfVector", {STATE_LIGHT, 0, STATE_HALF_VECTOR}, SWIZZLE_XYZW}, - {"spotDirection", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, - MAKE_SWIZZLE4(SWIZZLE_X, - SWIZZLE_Y, - SWIZZLE_Z, - SWIZZLE_Z)}, - {"spotCosCutoff", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_WWWW}, - {"spotCutoff", {STATE_LIGHT, 0, STATE_SPOT_CUTOFF}, SWIZZLE_XXXX}, - {"spotExponent", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_WWWW}, - {"constantAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_XXXX}, - {"linearAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_YYYY}, - {"quadraticAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_ZZZZ}, -}; - -static struct gl_builtin_uniform_element gl_LightModel_elements[] = { - {"ambient", {STATE_LIGHTMODEL_AMBIENT, 0}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_FrontLightModelProduct_elements[] = { - {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 0}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_BackLightModelProduct_elements[] = { - {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 1}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_FrontLightProduct_elements[] = { - {"ambient", {STATE_LIGHTPROD, 0, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"diffuse", {STATE_LIGHTPROD, 0, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"specular", {STATE_LIGHTPROD, 0, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_BackLightProduct_elements[] = { - {"ambient", {STATE_LIGHTPROD, 0, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, - {"diffuse", {STATE_LIGHTPROD, 0, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, - {"specular", {STATE_LIGHTPROD, 0, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_TextureEnvColor_elements[] = { - {NULL, {STATE_TEXENV_COLOR, 0}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_EyePlaneS_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_S}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_EyePlaneT_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_T}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_EyePlaneR_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_R}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_EyePlaneQ_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_Q}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_ObjectPlaneS_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_S}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_ObjectPlaneT_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_T}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_ObjectPlaneR_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_R}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_ObjectPlaneQ_elements[] = { - {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_Q}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_Fog_elements[] = { - {"color", {STATE_FOG_COLOR}, SWIZZLE_XYZW}, - {"density", {STATE_FOG_PARAMS}, SWIZZLE_XXXX}, - {"start", {STATE_FOG_PARAMS}, SWIZZLE_YYYY}, - {"end", {STATE_FOG_PARAMS}, SWIZZLE_ZZZZ}, - {"scale", {STATE_FOG_PARAMS}, SWIZZLE_WWWW}, -}; - -static struct gl_builtin_uniform_element gl_NormalScale_elements[] = { - {NULL, {STATE_NORMAL_SCALE}, SWIZZLE_XXXX}, -}; - -static struct gl_builtin_uniform_element gl_MESABumpRotMatrix0_elements[] = { - {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_0}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_MESABumpRotMatrix1_elements[] = { - {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_1}, SWIZZLE_XYZW}, -}; - -static struct gl_builtin_uniform_element gl_MESAFogParamsOptimized_elements[] = { - {NULL, {STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED}, SWIZZLE_XYZW}, -}; - -#define MATRIX(name, statevar, modifier) \ - static struct gl_builtin_uniform_element name ## _elements[] = { \ - { NULL, { statevar, 0, 0, 0, modifier}, SWIZZLE_XYZW }, \ - { NULL, { statevar, 0, 1, 1, modifier}, SWIZZLE_XYZW }, \ - { NULL, { statevar, 0, 2, 2, modifier}, SWIZZLE_XYZW }, \ - { NULL, { statevar, 0, 3, 3, modifier}, SWIZZLE_XYZW }, \ - } - -MATRIX(gl_ModelViewMatrix, - STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE); -MATRIX(gl_ModelViewMatrixInverse, - STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVTRANS); -MATRIX(gl_ModelViewMatrixTranspose, - STATE_MODELVIEW_MATRIX, 0); -MATRIX(gl_ModelViewMatrixInverseTranspose, - STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE); - -MATRIX(gl_ProjectionMatrix, - STATE_PROJECTION_MATRIX, STATE_MATRIX_TRANSPOSE); -MATRIX(gl_ProjectionMatrixInverse, - STATE_PROJECTION_MATRIX, STATE_MATRIX_INVTRANS); -MATRIX(gl_ProjectionMatrixTranspose, - STATE_PROJECTION_MATRIX, 0); -MATRIX(gl_ProjectionMatrixInverseTranspose, - STATE_PROJECTION_MATRIX, STATE_MATRIX_INVERSE); - -MATRIX(gl_ModelViewProjectionMatrix, - STATE_MVP_MATRIX, STATE_MATRIX_TRANSPOSE); -MATRIX(gl_ModelViewProjectionMatrixInverse, - STATE_MVP_MATRIX, STATE_MATRIX_INVTRANS); -MATRIX(gl_ModelViewProjectionMatrixTranspose, - STATE_MVP_MATRIX, 0); -MATRIX(gl_ModelViewProjectionMatrixInverseTranspose, - STATE_MVP_MATRIX, STATE_MATRIX_INVERSE); - -MATRIX(gl_TextureMatrix, - STATE_TEXTURE_MATRIX, STATE_MATRIX_TRANSPOSE); -MATRIX(gl_TextureMatrixInverse, - STATE_TEXTURE_MATRIX, STATE_MATRIX_INVTRANS); -MATRIX(gl_TextureMatrixTranspose, - STATE_TEXTURE_MATRIX, 0); -MATRIX(gl_TextureMatrixInverseTranspose, - STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE); - -static struct gl_builtin_uniform_element gl_NormalMatrix_elements[] = { - { NULL, { STATE_MODELVIEW_MATRIX, 0, 0, 0, STATE_MATRIX_INVERSE}, - SWIZZLE_XYZW }, - { NULL, { STATE_MODELVIEW_MATRIX, 0, 1, 1, STATE_MATRIX_INVERSE}, - SWIZZLE_XYZW }, - { NULL, { STATE_MODELVIEW_MATRIX, 0, 2, 2, STATE_MATRIX_INVERSE}, - SWIZZLE_XYZW }, -}; - -#undef MATRIX - -#define STATEVAR(name) {#name, name ## _elements, Elements(name ## _elements)} - -const struct gl_builtin_uniform_desc _mesa_builtin_uniform_desc[] = { - STATEVAR(gl_DepthRange), - STATEVAR(gl_ClipPlane), - STATEVAR(gl_Point), - STATEVAR(gl_FrontMaterial), - STATEVAR(gl_BackMaterial), - STATEVAR(gl_LightSource), - STATEVAR(gl_LightModel), - STATEVAR(gl_FrontLightModelProduct), - STATEVAR(gl_BackLightModelProduct), - STATEVAR(gl_FrontLightProduct), - STATEVAR(gl_BackLightProduct), - STATEVAR(gl_TextureEnvColor), - STATEVAR(gl_EyePlaneS), - STATEVAR(gl_EyePlaneT), - STATEVAR(gl_EyePlaneR), - STATEVAR(gl_EyePlaneQ), - STATEVAR(gl_ObjectPlaneS), - STATEVAR(gl_ObjectPlaneT), - STATEVAR(gl_ObjectPlaneR), - STATEVAR(gl_ObjectPlaneQ), - STATEVAR(gl_Fog), - - STATEVAR(gl_ModelViewMatrix), - STATEVAR(gl_ModelViewMatrixInverse), - STATEVAR(gl_ModelViewMatrixTranspose), - STATEVAR(gl_ModelViewMatrixInverseTranspose), - - STATEVAR(gl_ProjectionMatrix), - STATEVAR(gl_ProjectionMatrixInverse), - STATEVAR(gl_ProjectionMatrixTranspose), - STATEVAR(gl_ProjectionMatrixInverseTranspose), - - STATEVAR(gl_ModelViewProjectionMatrix), - STATEVAR(gl_ModelViewProjectionMatrixInverse), - STATEVAR(gl_ModelViewProjectionMatrixTranspose), - STATEVAR(gl_ModelViewProjectionMatrixInverseTranspose), - - STATEVAR(gl_TextureMatrix), - STATEVAR(gl_TextureMatrixInverse), - STATEVAR(gl_TextureMatrixTranspose), - STATEVAR(gl_TextureMatrixInverseTranspose), - - STATEVAR(gl_NormalMatrix), - STATEVAR(gl_NormalScale), - - STATEVAR(gl_MESABumpRotMatrix0), - STATEVAR(gl_MESABumpRotMatrix1), - STATEVAR(gl_MESAFogParamsOptimized), - - {NULL, NULL, 0} -}; - -static ir_variable * -add_variable(exec_list *instructions, glsl_symbol_table *symtab, - const char *name, const glsl_type *type, - enum ir_variable_mode mode, int slot) -{ - ir_variable *var = new(symtab) ir_variable(type, name, mode); - - switch (var->mode) { - case ir_var_auto: - case ir_var_in: - case ir_var_const_in: - case ir_var_uniform: - case ir_var_system_value: - var->read_only = true; - break; - case ir_var_inout: - case ir_var_out: - break; - default: - assert(0); - break; - } - - var->location = slot; - var->explicit_location = (slot >= 0); - - /* Once the variable is created an initialized, add it to the symbol table - * and add the declaration to the IR stream. - */ - instructions->push_tail(var); - - symtab->add_variable(var); - return var; -} - -static ir_variable * -add_uniform(exec_list *instructions, glsl_symbol_table *symtab, - const char *name, const glsl_type *type) -{ - ir_variable *const uni = - add_variable(instructions, symtab, name, type, ir_var_uniform, -1); - - unsigned i; - for (i = 0; _mesa_builtin_uniform_desc[i].name != NULL; i++) { - if (strcmp(_mesa_builtin_uniform_desc[i].name, name) == 0) { - break; - } - } - - assert(_mesa_builtin_uniform_desc[i].name != NULL); - const struct gl_builtin_uniform_desc* const statevar = - &_mesa_builtin_uniform_desc[i]; - - const unsigned array_count = type->is_array() ? type->length : 1; - uni->num_state_slots = array_count * statevar->num_elements; - - ir_state_slot *slots = - ralloc_array(uni, ir_state_slot, uni->num_state_slots); - - uni->state_slots = slots; - - for (unsigned a = 0; a < array_count; a++) { - for (unsigned j = 0; j < statevar->num_elements; j++) { - struct gl_builtin_uniform_element *element = &statevar->elements[j]; - - memcpy(slots->tokens, element->tokens, sizeof(element->tokens)); - if (type->is_array()) { - slots->tokens[1] = a; - } - - slots->swizzle = element->swizzle; - slots++; - } - } - - return uni; -} - -static void -add_builtin_variable(exec_list *instructions, glsl_symbol_table *symtab, - const builtin_variable *proto) -{ - /* Create a new variable declaration from the description supplied by - * the caller. - */ - const glsl_type *const type = symtab->get_type(proto->type); - - assert(type != NULL); - - if (proto->mode == ir_var_uniform) { - add_uniform(instructions, symtab, proto->name, type); - } else { - add_variable(instructions, symtab, proto->name, type, proto->mode, - proto->slot); - } -} - -static void -add_builtin_constant(exec_list *instructions, glsl_symbol_table *symtab, - const char *name, int value) -{ - ir_variable *const var = add_variable(instructions, symtab, - name, glsl_type::int_type, - ir_var_auto, -1); - var->constant_value = new(var) ir_constant(value); -} - -/* Several constants in GLSL ES have different names than normal desktop GLSL. - * Therefore, this function should only be called on the ES path. - */ -static void -generate_100ES_uniforms(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - glsl_symbol_table *const symtab = state->symbols; - - add_builtin_constant(instructions, symtab, "gl_MaxVertexAttribs", - state->Const.MaxVertexAttribs); - add_builtin_constant(instructions, symtab, "gl_MaxVertexUniformVectors", - state->Const.MaxVertexUniformComponents); - add_builtin_constant(instructions, symtab, "gl_MaxVaryingVectors", - state->Const.MaxVaryingFloats / 4); - add_builtin_constant(instructions, symtab, "gl_MaxVertexTextureImageUnits", - state->Const.MaxVertexTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxCombinedTextureImageUnits", - state->Const.MaxCombinedTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxTextureImageUnits", - state->Const.MaxTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxFragmentUniformVectors", - state->Const.MaxFragmentUniformComponents); - - add_uniform(instructions, symtab, "gl_DepthRange", - state->symbols->get_type("gl_DepthRangeParameters")); -} - -static void -generate_110_uniforms(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - glsl_symbol_table *const symtab = state->symbols; - - for (unsigned i = 0 - ; i < Elements(builtin_110_deprecated_uniforms) - ; i++) { - add_builtin_variable(instructions, symtab, - & builtin_110_deprecated_uniforms[i]); - } - - add_builtin_constant(instructions, symtab, "gl_MaxLights", - state->Const.MaxLights); - add_builtin_constant(instructions, symtab, "gl_MaxClipPlanes", - state->Const.MaxClipPlanes); - add_builtin_constant(instructions, symtab, "gl_MaxTextureUnits", - state->Const.MaxTextureUnits); - add_builtin_constant(instructions, symtab, "gl_MaxTextureCoords", - state->Const.MaxTextureCoords); - add_builtin_constant(instructions, symtab, "gl_MaxVertexAttribs", - state->Const.MaxVertexAttribs); - add_builtin_constant(instructions, symtab, "gl_MaxVertexUniformComponents", - state->Const.MaxVertexUniformComponents); - add_builtin_constant(instructions, symtab, "gl_MaxVaryingFloats", - state->Const.MaxVaryingFloats); - add_builtin_constant(instructions, symtab, "gl_MaxVertexTextureImageUnits", - state->Const.MaxVertexTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxCombinedTextureImageUnits", - state->Const.MaxCombinedTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxTextureImageUnits", - state->Const.MaxTextureImageUnits); - add_builtin_constant(instructions, symtab, "gl_MaxFragmentUniformComponents", - state->Const.MaxFragmentUniformComponents); - - const glsl_type *const mat4_array_type = - glsl_type::get_array_instance(glsl_type::mat4_type, - state->Const.MaxTextureCoords); - - add_uniform(instructions, symtab, "gl_TextureMatrix", mat4_array_type); - add_uniform(instructions, symtab, "gl_TextureMatrixInverse", mat4_array_type); - add_uniform(instructions, symtab, "gl_TextureMatrixTranspose", mat4_array_type); - add_uniform(instructions, symtab, "gl_TextureMatrixInverseTranspose", mat4_array_type); - - add_uniform(instructions, symtab, "gl_DepthRange", - symtab->get_type("gl_DepthRangeParameters")); - - add_uniform(instructions, symtab, "gl_ClipPlane", - glsl_type::get_array_instance(glsl_type::vec4_type, - state->Const.MaxClipPlanes)); - add_uniform(instructions, symtab, "gl_Point", - symtab->get_type("gl_PointParameters")); - - const glsl_type *const material_parameters_type = - symtab->get_type("gl_MaterialParameters"); - add_uniform(instructions, symtab, "gl_FrontMaterial", material_parameters_type); - add_uniform(instructions, symtab, "gl_BackMaterial", material_parameters_type); - - const glsl_type *const light_source_array_type = - glsl_type::get_array_instance(symtab->get_type("gl_LightSourceParameters"), state->Const.MaxLights); - - add_uniform(instructions, symtab, "gl_LightSource", light_source_array_type); - - const glsl_type *const light_model_products_type = - symtab->get_type("gl_LightModelProducts"); - add_uniform(instructions, symtab, "gl_FrontLightModelProduct", - light_model_products_type); - add_uniform(instructions, symtab, "gl_BackLightModelProduct", - light_model_products_type); - - const glsl_type *const light_products_type = - glsl_type::get_array_instance(symtab->get_type("gl_LightProducts"), - state->Const.MaxLights); - add_uniform(instructions, symtab, "gl_FrontLightProduct", light_products_type); - add_uniform(instructions, symtab, "gl_BackLightProduct", light_products_type); - - add_uniform(instructions, symtab, "gl_TextureEnvColor", - glsl_type::get_array_instance(glsl_type::vec4_type, - state->Const.MaxTextureUnits)); - - const glsl_type *const texcoords_vec4 = - glsl_type::get_array_instance(glsl_type::vec4_type, - state->Const.MaxTextureCoords); - add_uniform(instructions, symtab, "gl_EyePlaneS", texcoords_vec4); - add_uniform(instructions, symtab, "gl_EyePlaneT", texcoords_vec4); - add_uniform(instructions, symtab, "gl_EyePlaneR", texcoords_vec4); - add_uniform(instructions, symtab, "gl_EyePlaneQ", texcoords_vec4); - add_uniform(instructions, symtab, "gl_ObjectPlaneS", texcoords_vec4); - add_uniform(instructions, symtab, "gl_ObjectPlaneT", texcoords_vec4); - add_uniform(instructions, symtab, "gl_ObjectPlaneR", texcoords_vec4); - add_uniform(instructions, symtab, "gl_ObjectPlaneQ", texcoords_vec4); - - add_uniform(instructions, symtab, "gl_Fog", - symtab->get_type("gl_FogParameters")); -} - -/* This function should only be called for ES, not desktop GL. */ -static void -generate_100ES_vs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_core_vs_variables[i]); - } - - generate_100ES_uniforms(instructions, state); - - generate_ARB_draw_buffers_variables(instructions, state, false, - vertex_shader); -} - - -static void -generate_110_vs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_core_vs_variables[i]); - } - - for (unsigned i = 0 - ; i < Elements(builtin_110_deprecated_vs_variables) - ; i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_110_deprecated_vs_variables[i]); - } - generate_110_uniforms(instructions, state); - - /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: - * - * "As with all arrays, indices used to subscript gl_TexCoord must - * either be an integral constant expressions, or this array must be - * re-declared by the shader with a size. The size can be at most - * gl_MaxTextureCoords. Using indexes close to 0 may aid the - * implementation in preserving varying resources." - */ - const glsl_type *const vec4_array_type = - glsl_type::get_array_instance(glsl_type::vec4_type, 0); - - add_variable(instructions, state->symbols, - "gl_TexCoord", vec4_array_type, ir_var_out, VERT_RESULT_TEX0); - - generate_ARB_draw_buffers_variables(instructions, state, false, - vertex_shader); -} - - -static void -generate_120_vs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - /* GLSL version 1.20 did not add any built-in variables in the vertex - * shader. - */ - generate_110_vs_variables(instructions, state); -} - - -static void -generate_130_uniforms(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - glsl_symbol_table *const symtab = state->symbols; - - add_builtin_constant(instructions, symtab, "gl_MaxClipDistances", - state->Const.MaxClipPlanes); -} - - -static void -generate_130_vs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - generate_120_vs_variables(instructions, state); - - for (unsigned i = 0; i < Elements(builtin_130_vs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_130_vs_variables[i]); - } - - generate_130_uniforms(instructions, state); - - /* From the GLSL 1.30 spec, section 7.1 (Vertex Shader Special - * Variables): - * - * The gl_ClipDistance array is predeclared as unsized and must - * be sized by the shader either redeclaring it with a size or - * indexing it only with integral constant expressions. - * - * We represent this in Mesa by initially declaring the array as - * size 0. - */ - const glsl_type *const clip_distance_array_type = - glsl_type::get_array_instance(glsl_type::float_type, 0); - - /* FINISHME: gl_ClipDistance needs a real location assigned. */ - add_variable(instructions, state->symbols, - "gl_ClipDistance", clip_distance_array_type, ir_var_out, -1); - -} - - -static void -initialize_vs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - - switch (state->language_version) { - case 100: - generate_100ES_vs_variables(instructions, state); - break; - case 110: - generate_110_vs_variables(instructions, state); - break; - case 120: - generate_120_vs_variables(instructions, state); - break; - case 130: - generate_130_vs_variables(instructions, state); - break; - } - - if (state->ARB_draw_instanced_enable) - generate_ARB_draw_instanced_variables(instructions, state, false, - vertex_shader); -} - - -/* This function should only be called for ES, not desktop GL. */ -static void -generate_100ES_fs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_core_fs_variables[i]); - } - - for (unsigned i = 0; i < Elements(builtin_100ES_fs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_100ES_fs_variables[i]); - } - - generate_100ES_uniforms(instructions, state); - - generate_ARB_draw_buffers_variables(instructions, state, false, - fragment_shader); -} - -static void -generate_110_fs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_core_fs_variables[i]); - } - - for (unsigned i = 0; i < Elements(builtin_110_fs_variables); i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_110_fs_variables[i]); - } - - for (unsigned i = 0 - ; i < Elements(builtin_110_deprecated_fs_variables) - ; i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_110_deprecated_fs_variables[i]); - } - generate_110_uniforms(instructions, state); - - /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: - * - * "As with all arrays, indices used to subscript gl_TexCoord must - * either be an integral constant expressions, or this array must be - * re-declared by the shader with a size. The size can be at most - * gl_MaxTextureCoords. Using indexes close to 0 may aid the - * implementation in preserving varying resources." - */ - const glsl_type *const vec4_array_type = - glsl_type::get_array_instance(glsl_type::vec4_type, 0); - - add_variable(instructions, state->symbols, - "gl_TexCoord", vec4_array_type, ir_var_in, FRAG_ATTRIB_TEX0); - - generate_ARB_draw_buffers_variables(instructions, state, false, - fragment_shader); -} - - -static void -generate_ARB_draw_buffers_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state, - bool warn, _mesa_glsl_parser_targets target) -{ - /* gl_MaxDrawBuffers is available in all shader stages. - */ - ir_variable *const mdb = - add_variable(instructions, state->symbols, - "gl_MaxDrawBuffers", glsl_type::int_type, ir_var_auto, -1); - - if (warn) - mdb->warn_extension = "GL_ARB_draw_buffers"; - - mdb->constant_value = new(mdb) - ir_constant(int(state->Const.MaxDrawBuffers)); - - - /* gl_FragData is only available in the fragment shader. - */ - if (target == fragment_shader) { - const glsl_type *const vec4_array_type = - glsl_type::get_array_instance(glsl_type::vec4_type, - state->Const.MaxDrawBuffers); - - ir_variable *const fd = - add_variable(instructions, state->symbols, - "gl_FragData", vec4_array_type, - ir_var_out, FRAG_RESULT_DATA0); - - if (warn) - fd->warn_extension = "GL_ARB_draw_buffers"; - } -} - - -static void -generate_ARB_draw_instanced_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state, - bool warn, - _mesa_glsl_parser_targets target) -{ - /* gl_InstanceIDARB is only available in the vertex shader. - */ - if (target == vertex_shader) { - ir_variable *const inst = - add_variable(instructions, state->symbols, - "gl_InstanceIDARB", glsl_type::int_type, - ir_var_system_value, SYSTEM_VALUE_INSTANCE_ID); - - if (warn) - inst->warn_extension = "GL_ARB_draw_instanced"; - } -} - - -static void -generate_ARB_shader_stencil_export_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state, - bool warn) -{ - /* gl_FragStencilRefARB is only available in the fragment shader. - */ - ir_variable *const fd = - add_variable(instructions, state->symbols, - "gl_FragStencilRefARB", glsl_type::int_type, - ir_var_out, FRAG_RESULT_STENCIL); - - if (warn) - fd->warn_extension = "GL_ARB_shader_stencil_export"; -} - -static void -generate_AMD_shader_stencil_export_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state, - bool warn) -{ - /* gl_FragStencilRefAMD is only available in the fragment shader. - */ - ir_variable *const fd = - add_variable(instructions, state->symbols, - "gl_FragStencilRefAMD", glsl_type::int_type, - ir_var_out, FRAG_RESULT_STENCIL); - - if (warn) - fd->warn_extension = "GL_AMD_shader_stencil_export"; -} - -static void -generate_120_fs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - generate_110_fs_variables(instructions, state); - - for (unsigned i = 0 - ; i < Elements(builtin_120_fs_variables) - ; i++) { - add_builtin_variable(instructions, state->symbols, - & builtin_120_fs_variables[i]); - } -} - -static void -generate_130_fs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - generate_120_fs_variables(instructions, state); - - generate_130_uniforms(instructions, state); - - /* From the GLSL 1.30 spec, section 7.2 (Fragment Shader Special - * Variables): - * - * The built-in input variable gl_ClipDistance array contains linearly - * interpolated values for the vertex values written by the vertex shader - * to the gl_ClipDistance vertex output variable. This array must be - * sized in the fragment shader either implicitly or explicitly to be the - * same size as it was sized in the vertex shader. - * - * In other words, the array must be pre-declared as implicitly sized. We - * represent this in Mesa by initially declaring the array as size 0. - */ - const glsl_type *const clip_distance_array_type = - glsl_type::get_array_instance(glsl_type::float_type, 0); - - /* FINISHME: gl_ClipDistance needs a real location assigned. */ - add_variable(instructions, state->symbols, - "gl_ClipDistance", clip_distance_array_type, ir_var_in, -1); -} - -static void -initialize_fs_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - - switch (state->language_version) { - case 100: - generate_100ES_fs_variables(instructions, state); - break; - case 110: - generate_110_fs_variables(instructions, state); - break; - case 120: - generate_120_fs_variables(instructions, state); - break; - case 130: - generate_130_fs_variables(instructions, state); - break; - } - - if (state->ARB_shader_stencil_export_enable) - generate_ARB_shader_stencil_export_variables(instructions, state, - state->ARB_shader_stencil_export_warn); - - if (state->AMD_shader_stencil_export_enable) - generate_AMD_shader_stencil_export_variables(instructions, state, - state->AMD_shader_stencil_export_warn); -} - -void -_mesa_glsl_initialize_variables(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - switch (state->target) { - case vertex_shader: - initialize_vs_variables(instructions, state); - break; - case geometry_shader: - break; - case fragment_shader: - initialize_fs_variables(instructions, state); - break; - } -} +/* + * 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 "ir.h" +#include "glsl_parser_extras.h" +#include "glsl_symbol_table.h" +#include "builtin_variables.h" +#include "main/uniforms.h" +#include "program/prog_parameter.h" +#include "program/prog_statevars.h" +#include "program/prog_instruction.h" + +static void generate_ARB_draw_buffers_variables(exec_list *, + struct _mesa_glsl_parse_state *, + bool, _mesa_glsl_parser_targets); + +static void +generate_ARB_draw_instanced_variables(exec_list *, + struct _mesa_glsl_parse_state *, + bool, _mesa_glsl_parser_targets); + +static struct gl_builtin_uniform_element gl_DepthRange_elements[] = { + {"near", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_XXXX}, + {"far", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_YYYY}, + {"diff", {STATE_DEPTH_RANGE, 0, 0}, SWIZZLE_ZZZZ}, +}; + +static struct gl_builtin_uniform_element gl_ClipPlane_elements[] = { + {NULL, {STATE_CLIPPLANE, 0, 0}, SWIZZLE_XYZW} +}; + +static struct gl_builtin_uniform_element gl_Point_elements[] = { + {"size", {STATE_POINT_SIZE}, SWIZZLE_XXXX}, + {"sizeMin", {STATE_POINT_SIZE}, SWIZZLE_YYYY}, + {"sizeMax", {STATE_POINT_SIZE}, SWIZZLE_ZZZZ}, + {"fadeThresholdSize", {STATE_POINT_SIZE}, SWIZZLE_WWWW}, + {"distanceConstantAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_XXXX}, + {"distanceLinearAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_YYYY}, + {"distanceQuadraticAttenuation", {STATE_POINT_ATTENUATION}, SWIZZLE_ZZZZ}, +}; + +static struct gl_builtin_uniform_element gl_FrontMaterial_elements[] = { + {"emission", {STATE_MATERIAL, 0, STATE_EMISSION}, SWIZZLE_XYZW}, + {"ambient", {STATE_MATERIAL, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_MATERIAL, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_MATERIAL, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"shininess", {STATE_MATERIAL, 0, STATE_SHININESS}, SWIZZLE_XXXX}, +}; + +static struct gl_builtin_uniform_element gl_BackMaterial_elements[] = { + {"emission", {STATE_MATERIAL, 1, STATE_EMISSION}, SWIZZLE_XYZW}, + {"ambient", {STATE_MATERIAL, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_MATERIAL, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_MATERIAL, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"shininess", {STATE_MATERIAL, 1, STATE_SHININESS}, SWIZZLE_XXXX}, +}; + +static struct gl_builtin_uniform_element gl_LightSource_elements[] = { + {"ambient", {STATE_LIGHT, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHT, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHT, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, + {"position", {STATE_LIGHT, 0, STATE_POSITION}, SWIZZLE_XYZW}, + {"halfVector", {STATE_LIGHT, 0, STATE_HALF_VECTOR}, SWIZZLE_XYZW}, + {"spotDirection", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, + MAKE_SWIZZLE4(SWIZZLE_X, + SWIZZLE_Y, + SWIZZLE_Z, + SWIZZLE_Z)}, + {"spotCosCutoff", {STATE_LIGHT, 0, STATE_SPOT_DIRECTION}, SWIZZLE_WWWW}, + {"spotCutoff", {STATE_LIGHT, 0, STATE_SPOT_CUTOFF}, SWIZZLE_XXXX}, + {"spotExponent", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_WWWW}, + {"constantAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_XXXX}, + {"linearAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_YYYY}, + {"quadraticAttenuation", {STATE_LIGHT, 0, STATE_ATTENUATION}, SWIZZLE_ZZZZ}, +}; + +static struct gl_builtin_uniform_element gl_LightModel_elements[] = { + {"ambient", {STATE_LIGHTMODEL_AMBIENT, 0}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_FrontLightModelProduct_elements[] = { + {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 0}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_BackLightModelProduct_elements[] = { + {"sceneColor", {STATE_LIGHTMODEL_SCENECOLOR, 1}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_FrontLightProduct_elements[] = { + {"ambient", {STATE_LIGHTPROD, 0, 0, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHTPROD, 0, 0, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHTPROD, 0, 0, STATE_SPECULAR}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_BackLightProduct_elements[] = { + {"ambient", {STATE_LIGHTPROD, 0, 1, STATE_AMBIENT}, SWIZZLE_XYZW}, + {"diffuse", {STATE_LIGHTPROD, 0, 1, STATE_DIFFUSE}, SWIZZLE_XYZW}, + {"specular", {STATE_LIGHTPROD, 0, 1, STATE_SPECULAR}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_TextureEnvColor_elements[] = { + {NULL, {STATE_TEXENV_COLOR, 0}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_EyePlaneS_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_S}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_EyePlaneT_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_T}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_EyePlaneR_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_R}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_EyePlaneQ_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_EYE_Q}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_ObjectPlaneS_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_S}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_ObjectPlaneT_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_T}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_ObjectPlaneR_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_R}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_ObjectPlaneQ_elements[] = { + {NULL, {STATE_TEXGEN, 0, STATE_TEXGEN_OBJECT_Q}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_Fog_elements[] = { + {"color", {STATE_FOG_COLOR}, SWIZZLE_XYZW}, + {"density", {STATE_FOG_PARAMS}, SWIZZLE_XXXX}, + {"start", {STATE_FOG_PARAMS}, SWIZZLE_YYYY}, + {"end", {STATE_FOG_PARAMS}, SWIZZLE_ZZZZ}, + {"scale", {STATE_FOG_PARAMS}, SWIZZLE_WWWW}, +}; + +static struct gl_builtin_uniform_element gl_NormalScale_elements[] = { + {NULL, {STATE_NORMAL_SCALE}, SWIZZLE_XXXX}, +}; + +static struct gl_builtin_uniform_element gl_MESABumpRotMatrix0_elements[] = { + {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_0}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_MESABumpRotMatrix1_elements[] = { + {NULL, {STATE_INTERNAL, STATE_ROT_MATRIX_1}, SWIZZLE_XYZW}, +}; + +static struct gl_builtin_uniform_element gl_MESAFogParamsOptimized_elements[] = { + {NULL, {STATE_INTERNAL, STATE_FOG_PARAMS_OPTIMIZED}, SWIZZLE_XYZW}, +}; + +#define MATRIX(name, statevar, modifier) \ + static struct gl_builtin_uniform_element name ## _elements[] = { \ + { NULL, { statevar, 0, 0, 0, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 1, 1, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 2, 2, modifier}, SWIZZLE_XYZW }, \ + { NULL, { statevar, 0, 3, 3, modifier}, SWIZZLE_XYZW }, \ + } + +MATRIX(gl_ModelViewMatrix, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ModelViewMatrixInverse, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ModelViewMatrixTranspose, + STATE_MODELVIEW_MATRIX, 0); +MATRIX(gl_ModelViewMatrixInverseTranspose, + STATE_MODELVIEW_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_ProjectionMatrix, + STATE_PROJECTION_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ProjectionMatrixInverse, + STATE_PROJECTION_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ProjectionMatrixTranspose, + STATE_PROJECTION_MATRIX, 0); +MATRIX(gl_ProjectionMatrixInverseTranspose, + STATE_PROJECTION_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_ModelViewProjectionMatrix, + STATE_MVP_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_ModelViewProjectionMatrixInverse, + STATE_MVP_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_ModelViewProjectionMatrixTranspose, + STATE_MVP_MATRIX, 0); +MATRIX(gl_ModelViewProjectionMatrixInverseTranspose, + STATE_MVP_MATRIX, STATE_MATRIX_INVERSE); + +MATRIX(gl_TextureMatrix, + STATE_TEXTURE_MATRIX, STATE_MATRIX_TRANSPOSE); +MATRIX(gl_TextureMatrixInverse, + STATE_TEXTURE_MATRIX, STATE_MATRIX_INVTRANS); +MATRIX(gl_TextureMatrixTranspose, + STATE_TEXTURE_MATRIX, 0); +MATRIX(gl_TextureMatrixInverseTranspose, + STATE_TEXTURE_MATRIX, STATE_MATRIX_INVERSE); + +static struct gl_builtin_uniform_element gl_NormalMatrix_elements[] = { + { NULL, { STATE_MODELVIEW_MATRIX, 0, 0, 0, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, + { NULL, { STATE_MODELVIEW_MATRIX, 0, 1, 1, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, + { NULL, { STATE_MODELVIEW_MATRIX, 0, 2, 2, STATE_MATRIX_INVERSE}, + SWIZZLE_XYZW }, +}; + +#undef MATRIX + +#define STATEVAR(name) {#name, name ## _elements, Elements(name ## _elements)} + +const struct gl_builtin_uniform_desc _mesa_builtin_uniform_desc[] = { + STATEVAR(gl_DepthRange), + STATEVAR(gl_ClipPlane), + STATEVAR(gl_Point), + STATEVAR(gl_FrontMaterial), + STATEVAR(gl_BackMaterial), + STATEVAR(gl_LightSource), + STATEVAR(gl_LightModel), + STATEVAR(gl_FrontLightModelProduct), + STATEVAR(gl_BackLightModelProduct), + STATEVAR(gl_FrontLightProduct), + STATEVAR(gl_BackLightProduct), + STATEVAR(gl_TextureEnvColor), + STATEVAR(gl_EyePlaneS), + STATEVAR(gl_EyePlaneT), + STATEVAR(gl_EyePlaneR), + STATEVAR(gl_EyePlaneQ), + STATEVAR(gl_ObjectPlaneS), + STATEVAR(gl_ObjectPlaneT), + STATEVAR(gl_ObjectPlaneR), + STATEVAR(gl_ObjectPlaneQ), + STATEVAR(gl_Fog), + + STATEVAR(gl_ModelViewMatrix), + STATEVAR(gl_ModelViewMatrixInverse), + STATEVAR(gl_ModelViewMatrixTranspose), + STATEVAR(gl_ModelViewMatrixInverseTranspose), + + STATEVAR(gl_ProjectionMatrix), + STATEVAR(gl_ProjectionMatrixInverse), + STATEVAR(gl_ProjectionMatrixTranspose), + STATEVAR(gl_ProjectionMatrixInverseTranspose), + + STATEVAR(gl_ModelViewProjectionMatrix), + STATEVAR(gl_ModelViewProjectionMatrixInverse), + STATEVAR(gl_ModelViewProjectionMatrixTranspose), + STATEVAR(gl_ModelViewProjectionMatrixInverseTranspose), + + STATEVAR(gl_TextureMatrix), + STATEVAR(gl_TextureMatrixInverse), + STATEVAR(gl_TextureMatrixTranspose), + STATEVAR(gl_TextureMatrixInverseTranspose), + + STATEVAR(gl_NormalMatrix), + STATEVAR(gl_NormalScale), + + STATEVAR(gl_MESABumpRotMatrix0), + STATEVAR(gl_MESABumpRotMatrix1), + STATEVAR(gl_MESAFogParamsOptimized), + + {NULL, NULL, 0} +}; + +static ir_variable * +add_variable(exec_list *instructions, glsl_symbol_table *symtab, + const char *name, const glsl_type *type, + enum ir_variable_mode mode, int slot) +{ + ir_variable *var = new(symtab) ir_variable(type, name, mode); + + switch (var->mode) { + case ir_var_auto: + case ir_var_in: + case ir_var_const_in: + case ir_var_uniform: + case ir_var_system_value: + var->read_only = true; + break; + case ir_var_inout: + case ir_var_out: + break; + default: + assert(0); + break; + } + + var->location = slot; + var->explicit_location = (slot >= 0); + + /* Once the variable is created an initialized, add it to the symbol table + * and add the declaration to the IR stream. + */ + instructions->push_tail(var); + + symtab->add_variable(var); + return var; +} + +static ir_variable * +add_uniform(exec_list *instructions, glsl_symbol_table *symtab, + const char *name, const glsl_type *type) +{ + ir_variable *const uni = + add_variable(instructions, symtab, name, type, ir_var_uniform, -1); + + unsigned i; + for (i = 0; _mesa_builtin_uniform_desc[i].name != NULL; i++) { + if (strcmp(_mesa_builtin_uniform_desc[i].name, name) == 0) { + break; + } + } + + assert(_mesa_builtin_uniform_desc[i].name != NULL); + const struct gl_builtin_uniform_desc* const statevar = + &_mesa_builtin_uniform_desc[i]; + + const unsigned array_count = type->is_array() ? type->length : 1; + uni->num_state_slots = array_count * statevar->num_elements; + + ir_state_slot *slots = + ralloc_array(uni, ir_state_slot, uni->num_state_slots); + + uni->state_slots = slots; + + for (unsigned a = 0; a < array_count; a++) { + for (unsigned j = 0; j < statevar->num_elements; j++) { + struct gl_builtin_uniform_element *element = &statevar->elements[j]; + + memcpy(slots->tokens, element->tokens, sizeof(element->tokens)); + if (type->is_array()) { + slots->tokens[1] = a; + } + + slots->swizzle = element->swizzle; + slots++; + } + } + + return uni; +} + +static void +add_builtin_variable(exec_list *instructions, glsl_symbol_table *symtab, + const builtin_variable *proto) +{ + /* Create a new variable declaration from the description supplied by + * the caller. + */ + const glsl_type *const type = symtab->get_type(proto->type); + + assert(type != NULL); + + if (proto->mode == ir_var_uniform) { + add_uniform(instructions, symtab, proto->name, type); + } else { + add_variable(instructions, symtab, proto->name, type, proto->mode, + proto->slot); + } +} + +static void +add_builtin_constant(exec_list *instructions, glsl_symbol_table *symtab, + const char *name, int value) +{ + ir_variable *const var = add_variable(instructions, symtab, + name, glsl_type::int_type, + ir_var_auto, -1); + var->constant_value = new(var) ir_constant(value); +} + +/* Several constants in GLSL ES have different names than normal desktop GLSL. + * Therefore, this function should only be called on the ES path. + */ +static void +generate_100ES_uniforms(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + glsl_symbol_table *const symtab = state->symbols; + + add_builtin_constant(instructions, symtab, "gl_MaxVertexAttribs", + state->Const.MaxVertexAttribs); + add_builtin_constant(instructions, symtab, "gl_MaxVertexUniformVectors", + state->Const.MaxVertexUniformComponents); + add_builtin_constant(instructions, symtab, "gl_MaxVaryingVectors", + state->Const.MaxVaryingFloats / 4); + add_builtin_constant(instructions, symtab, "gl_MaxVertexTextureImageUnits", + state->Const.MaxVertexTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxCombinedTextureImageUnits", + state->Const.MaxCombinedTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxTextureImageUnits", + state->Const.MaxTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxFragmentUniformVectors", + state->Const.MaxFragmentUniformComponents); + + add_uniform(instructions, symtab, "gl_DepthRange", + state->symbols->get_type("gl_DepthRangeParameters")); +} + +static void +generate_110_uniforms(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + glsl_symbol_table *const symtab = state->symbols; + + for (unsigned i = 0 + ; i < Elements(builtin_110_deprecated_uniforms) + ; i++) { + add_builtin_variable(instructions, symtab, + & builtin_110_deprecated_uniforms[i]); + } + + add_builtin_constant(instructions, symtab, "gl_MaxLights", + state->Const.MaxLights); + add_builtin_constant(instructions, symtab, "gl_MaxClipPlanes", + state->Const.MaxClipPlanes); + add_builtin_constant(instructions, symtab, "gl_MaxTextureUnits", + state->Const.MaxTextureUnits); + add_builtin_constant(instructions, symtab, "gl_MaxTextureCoords", + state->Const.MaxTextureCoords); + add_builtin_constant(instructions, symtab, "gl_MaxVertexAttribs", + state->Const.MaxVertexAttribs); + add_builtin_constant(instructions, symtab, "gl_MaxVertexUniformComponents", + state->Const.MaxVertexUniformComponents); + add_builtin_constant(instructions, symtab, "gl_MaxVaryingFloats", + state->Const.MaxVaryingFloats); + add_builtin_constant(instructions, symtab, "gl_MaxVertexTextureImageUnits", + state->Const.MaxVertexTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxCombinedTextureImageUnits", + state->Const.MaxCombinedTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxTextureImageUnits", + state->Const.MaxTextureImageUnits); + add_builtin_constant(instructions, symtab, "gl_MaxFragmentUniformComponents", + state->Const.MaxFragmentUniformComponents); + + const glsl_type *const mat4_array_type = + glsl_type::get_array_instance(glsl_type::mat4_type, + state->Const.MaxTextureCoords); + + add_uniform(instructions, symtab, "gl_TextureMatrix", mat4_array_type); + add_uniform(instructions, symtab, "gl_TextureMatrixInverse", mat4_array_type); + add_uniform(instructions, symtab, "gl_TextureMatrixTranspose", mat4_array_type); + add_uniform(instructions, symtab, "gl_TextureMatrixInverseTranspose", mat4_array_type); + + add_uniform(instructions, symtab, "gl_DepthRange", + symtab->get_type("gl_DepthRangeParameters")); + + add_uniform(instructions, symtab, "gl_ClipPlane", + glsl_type::get_array_instance(glsl_type::vec4_type, + state->Const.MaxClipPlanes)); + add_uniform(instructions, symtab, "gl_Point", + symtab->get_type("gl_PointParameters")); + + const glsl_type *const material_parameters_type = + symtab->get_type("gl_MaterialParameters"); + add_uniform(instructions, symtab, "gl_FrontMaterial", material_parameters_type); + add_uniform(instructions, symtab, "gl_BackMaterial", material_parameters_type); + + const glsl_type *const light_source_array_type = + glsl_type::get_array_instance(symtab->get_type("gl_LightSourceParameters"), state->Const.MaxLights); + + add_uniform(instructions, symtab, "gl_LightSource", light_source_array_type); + + const glsl_type *const light_model_products_type = + symtab->get_type("gl_LightModelProducts"); + add_uniform(instructions, symtab, "gl_FrontLightModelProduct", + light_model_products_type); + add_uniform(instructions, symtab, "gl_BackLightModelProduct", + light_model_products_type); + + const glsl_type *const light_products_type = + glsl_type::get_array_instance(symtab->get_type("gl_LightProducts"), + state->Const.MaxLights); + add_uniform(instructions, symtab, "gl_FrontLightProduct", light_products_type); + add_uniform(instructions, symtab, "gl_BackLightProduct", light_products_type); + + add_uniform(instructions, symtab, "gl_TextureEnvColor", + glsl_type::get_array_instance(glsl_type::vec4_type, + state->Const.MaxTextureUnits)); + + const glsl_type *const texcoords_vec4 = + glsl_type::get_array_instance(glsl_type::vec4_type, + state->Const.MaxTextureCoords); + add_uniform(instructions, symtab, "gl_EyePlaneS", texcoords_vec4); + add_uniform(instructions, symtab, "gl_EyePlaneT", texcoords_vec4); + add_uniform(instructions, symtab, "gl_EyePlaneR", texcoords_vec4); + add_uniform(instructions, symtab, "gl_EyePlaneQ", texcoords_vec4); + add_uniform(instructions, symtab, "gl_ObjectPlaneS", texcoords_vec4); + add_uniform(instructions, symtab, "gl_ObjectPlaneT", texcoords_vec4); + add_uniform(instructions, symtab, "gl_ObjectPlaneR", texcoords_vec4); + add_uniform(instructions, symtab, "gl_ObjectPlaneQ", texcoords_vec4); + + add_uniform(instructions, symtab, "gl_Fog", + symtab->get_type("gl_FogParameters")); +} + +/* This function should only be called for ES, not desktop GL. */ +static void +generate_100ES_vs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_core_vs_variables[i]); + } + + generate_100ES_uniforms(instructions, state); + + generate_ARB_draw_buffers_variables(instructions, state, false, + vertex_shader); +} + + +static void +generate_110_vs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + for (unsigned i = 0; i < Elements(builtin_core_vs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_core_vs_variables[i]); + } + + for (unsigned i = 0 + ; i < Elements(builtin_110_deprecated_vs_variables) + ; i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_110_deprecated_vs_variables[i]); + } + generate_110_uniforms(instructions, state); + + /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: + * + * "As with all arrays, indices used to subscript gl_TexCoord must + * either be an integral constant expressions, or this array must be + * re-declared by the shader with a size. The size can be at most + * gl_MaxTextureCoords. Using indexes close to 0 may aid the + * implementation in preserving varying resources." + */ + const glsl_type *const vec4_array_type = + glsl_type::get_array_instance(glsl_type::vec4_type, 0); + + add_variable(instructions, state->symbols, + "gl_TexCoord", vec4_array_type, ir_var_out, VERT_RESULT_TEX0); + + generate_ARB_draw_buffers_variables(instructions, state, false, + vertex_shader); +} + + +static void +generate_120_vs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + /* GLSL version 1.20 did not add any built-in variables in the vertex + * shader. + */ + generate_110_vs_variables(instructions, state); +} + + +static void +generate_130_uniforms(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + glsl_symbol_table *const symtab = state->symbols; + + add_builtin_constant(instructions, symtab, "gl_MaxClipDistances", + state->Const.MaxClipPlanes); +} + + +static void +generate_130_vs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + generate_120_vs_variables(instructions, state); + + for (unsigned i = 0; i < Elements(builtin_130_vs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_130_vs_variables[i]); + } + + generate_130_uniforms(instructions, state); + + /* From the GLSL 1.30 spec, section 7.1 (Vertex Shader Special + * Variables): + * + * The gl_ClipDistance array is predeclared as unsized and must + * be sized by the shader either redeclaring it with a size or + * indexing it only with integral constant expressions. + * + * We represent this in Mesa by initially declaring the array as + * size 0. + */ + const glsl_type *const clip_distance_array_type = + glsl_type::get_array_instance(glsl_type::float_type, 0); + + /* FINISHME: gl_ClipDistance needs a real location assigned. */ + add_variable(instructions, state->symbols, + "gl_ClipDistance", clip_distance_array_type, ir_var_out, -1); + +} + + +static void +initialize_vs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + + switch (state->language_version) { + case 100: + generate_100ES_vs_variables(instructions, state); + break; + case 110: + generate_110_vs_variables(instructions, state); + break; + case 120: + generate_120_vs_variables(instructions, state); + break; + case 130: + generate_130_vs_variables(instructions, state); + break; + } + + if (state->ARB_draw_instanced_enable) + generate_ARB_draw_instanced_variables(instructions, state, false, + vertex_shader); +} + + +/* This function should only be called for ES, not desktop GL. */ +static void +generate_100ES_fs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_core_fs_variables[i]); + } + + for (unsigned i = 0; i < Elements(builtin_100ES_fs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_100ES_fs_variables[i]); + } + + generate_100ES_uniforms(instructions, state); + + generate_ARB_draw_buffers_variables(instructions, state, false, + fragment_shader); +} + +static void +generate_110_fs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + for (unsigned i = 0; i < Elements(builtin_core_fs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_core_fs_variables[i]); + } + + for (unsigned i = 0; i < Elements(builtin_110_fs_variables); i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_110_fs_variables[i]); + } + + for (unsigned i = 0 + ; i < Elements(builtin_110_deprecated_fs_variables) + ; i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_110_deprecated_fs_variables[i]); + } + generate_110_uniforms(instructions, state); + + /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: + * + * "As with all arrays, indices used to subscript gl_TexCoord must + * either be an integral constant expressions, or this array must be + * re-declared by the shader with a size. The size can be at most + * gl_MaxTextureCoords. Using indexes close to 0 may aid the + * implementation in preserving varying resources." + */ + const glsl_type *const vec4_array_type = + glsl_type::get_array_instance(glsl_type::vec4_type, 0); + + add_variable(instructions, state->symbols, + "gl_TexCoord", vec4_array_type, ir_var_in, FRAG_ATTRIB_TEX0); + + generate_ARB_draw_buffers_variables(instructions, state, false, + fragment_shader); +} + + +static void +generate_ARB_draw_buffers_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state, + bool warn, _mesa_glsl_parser_targets target) +{ + /* gl_MaxDrawBuffers is available in all shader stages. + */ + ir_variable *const mdb = + add_variable(instructions, state->symbols, + "gl_MaxDrawBuffers", glsl_type::int_type, ir_var_auto, -1); + + if (warn) + mdb->warn_extension = "GL_ARB_draw_buffers"; + + mdb->constant_value = new(mdb) + ir_constant(int(state->Const.MaxDrawBuffers)); + + + /* gl_FragData is only available in the fragment shader. + */ + if (target == fragment_shader) { + const glsl_type *const vec4_array_type = + glsl_type::get_array_instance(glsl_type::vec4_type, + state->Const.MaxDrawBuffers); + + ir_variable *const fd = + add_variable(instructions, state->symbols, + "gl_FragData", vec4_array_type, + ir_var_out, FRAG_RESULT_DATA0); + + if (warn) + fd->warn_extension = "GL_ARB_draw_buffers"; + } +} + + +static void +generate_ARB_draw_instanced_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state, + bool warn, + _mesa_glsl_parser_targets target) +{ + /* gl_InstanceIDARB is only available in the vertex shader. + */ + if (target == vertex_shader) { + ir_variable *const inst = + add_variable(instructions, state->symbols, + "gl_InstanceIDARB", glsl_type::int_type, + ir_var_system_value, SYSTEM_VALUE_INSTANCE_ID); + + if (warn) + inst->warn_extension = "GL_ARB_draw_instanced"; + } +} + + +static void +generate_ARB_shader_stencil_export_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state, + bool warn) +{ + /* gl_FragStencilRefARB is only available in the fragment shader. + */ + ir_variable *const fd = + add_variable(instructions, state->symbols, + "gl_FragStencilRefARB", glsl_type::int_type, + ir_var_out, FRAG_RESULT_STENCIL); + + if (warn) + fd->warn_extension = "GL_ARB_shader_stencil_export"; +} + +static void +generate_AMD_shader_stencil_export_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state, + bool warn) +{ + /* gl_FragStencilRefAMD is only available in the fragment shader. + */ + ir_variable *const fd = + add_variable(instructions, state->symbols, + "gl_FragStencilRefAMD", glsl_type::int_type, + ir_var_out, FRAG_RESULT_STENCIL); + + if (warn) + fd->warn_extension = "GL_AMD_shader_stencil_export"; +} + +static void +generate_120_fs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + generate_110_fs_variables(instructions, state); + + for (unsigned i = 0 + ; i < Elements(builtin_120_fs_variables) + ; i++) { + add_builtin_variable(instructions, state->symbols, + & builtin_120_fs_variables[i]); + } +} + +static void +generate_130_fs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + generate_120_fs_variables(instructions, state); + + generate_130_uniforms(instructions, state); + + /* From the GLSL 1.30 spec, section 7.2 (Fragment Shader Special + * Variables): + * + * The built-in input variable gl_ClipDistance array contains linearly + * interpolated values for the vertex values written by the vertex shader + * to the gl_ClipDistance vertex output variable. This array must be + * sized in the fragment shader either implicitly or explicitly to be the + * same size as it was sized in the vertex shader. + * + * In other words, the array must be pre-declared as implicitly sized. We + * represent this in Mesa by initially declaring the array as size 0. + */ + const glsl_type *const clip_distance_array_type = + glsl_type::get_array_instance(glsl_type::float_type, 0); + + /* FINISHME: gl_ClipDistance needs a real location assigned. */ + add_variable(instructions, state->symbols, + "gl_ClipDistance", clip_distance_array_type, ir_var_in, -1); +} + +static void +initialize_fs_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + + switch (state->language_version) { + case 100: + generate_100ES_fs_variables(instructions, state); + break; + case 110: + generate_110_fs_variables(instructions, state); + break; + case 120: + generate_120_fs_variables(instructions, state); + break; + case 130: + generate_130_fs_variables(instructions, state); + break; + } + + if (state->ARB_shader_stencil_export_enable) + generate_ARB_shader_stencil_export_variables(instructions, state, + state->ARB_shader_stencil_export_warn); + + if (state->AMD_shader_stencil_export_enable) + generate_AMD_shader_stencil_export_variables(instructions, state, + state->AMD_shader_stencil_export_warn); +} + +void +_mesa_glsl_initialize_variables(exec_list *instructions, + struct _mesa_glsl_parse_state *state) +{ + switch (state->target) { + case vertex_shader: + initialize_vs_variables(instructions, state); + break; + case geometry_shader: + break; + case fragment_shader: + initialize_fs_variables(instructions, state); + break; + } +} diff --git a/mesalib/src/glsl/link_functions.cpp b/mesalib/src/glsl/link_functions.cpp index 68d5b287b..acee32712 100644 --- a/mesalib/src/glsl/link_functions.cpp +++ b/mesalib/src/glsl/link_functions.cpp @@ -1,286 +1,286 @@ -/* - * 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 "main/core.h" -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "ir.h" -#include "program.h" -#include "program/hash_table.h" -#include "linker.h" - -static ir_function_signature * -find_matching_signature(const char *name, const exec_list *actual_parameters, - gl_shader **shader_list, unsigned num_shaders, - bool use_builtin); - -class call_link_visitor : public ir_hierarchical_visitor { -public: - call_link_visitor(gl_shader_program *prog, gl_shader *linked, - gl_shader **shader_list, unsigned num_shaders) - { - this->prog = prog; - this->shader_list = shader_list; - this->num_shaders = num_shaders; - this->success = true; - this->linked = linked; - - this->locals = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - } - - ~call_link_visitor() - { - hash_table_dtor(this->locals); - } - - virtual ir_visitor_status visit(ir_variable *ir) - { - hash_table_insert(locals, ir, ir); - return visit_continue; - } - - virtual ir_visitor_status visit_enter(ir_call *ir) - { - /* If ir is an ir_call from a function that was imported from another - * shader callee will point to an ir_function_signature in the original - * shader. In this case the function signature MUST NOT BE MODIFIED. - * Doing so will modify the original shader. This may prevent that - * shader from being linkable in other programs. - */ - const ir_function_signature *const callee = ir->get_callee(); - assert(callee != NULL); - const char *const name = callee->function_name(); - - /* Determine if the requested function signature already exists in the - * final linked shader. If it does, use it as the target of the call. - */ - ir_function_signature *sig = - find_matching_signature(name, &callee->parameters, &linked, 1, - ir->use_builtin); - if (sig != NULL) { - ir->set_callee(sig); - return visit_continue; - } - - /* Try to find the signature in one of the other shaders that is being - * linked. If it's not found there, return an error. - */ - sig = find_matching_signature(name, &ir->actual_parameters, shader_list, - num_shaders, ir->use_builtin); - if (sig == NULL) { - /* FINISHME: Log the full signature of unresolved function. - */ - linker_error(this->prog, "unresolved reference to function `%s'\n", - name); - this->success = false; - return visit_stop; - } - - /* Find the prototype information in the linked shader. Generate any - * details that may be missing. - */ - ir_function *f = linked->symbols->get_function(name); - if (f == NULL) { - f = new(linked) ir_function(name); - - /* Add the new function to the linked IR. Put it at the end - * so that it comes after any global variable declarations - * that it refers to. - */ - linked->symbols->add_function(f); - linked->ir->push_tail(f); - } - - ir_function_signature *linked_sig = - f->exact_matching_signature(&callee->parameters); - if ((linked_sig == NULL) - || ((linked_sig != NULL) - && (linked_sig->is_builtin != ir->use_builtin))) { - linked_sig = new(linked) ir_function_signature(callee->return_type); - f->add_signature(linked_sig); - } - - /* At this point linked_sig and called may be the same. If ir is an - * ir_call from linked then linked_sig and callee will be - * ir_function_signatures that have no definitions (is_defined is false). - */ - assert(!linked_sig->is_defined); - assert(linked_sig->body.is_empty()); - - /* Create an in-place clone of the function definition. This multistep - * process introduces some complexity here, but it has some advantages. - * The parameter list and the and function body are cloned separately. - * The clone of the parameter list is used to prime the hashtable used - * to replace variable references in the cloned body. - * - * The big advantage is that the ir_function_signature does not change. - * This means that we don't have to process the rest of the IR tree to - * patch ir_call nodes. In addition, there is no way to remove or - * replace signature stored in a function. One could easily be added, - * but this avoids the need. - */ - struct hash_table *ht = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - exec_list formal_parameters; - foreach_list_const(node, &sig->parameters) { - const ir_instruction *const original = (ir_instruction *) node; - assert(const_cast(original)->as_variable()); - - ir_instruction *copy = original->clone(linked, ht); - formal_parameters.push_tail(copy); - } - - linked_sig->replace_parameters(&formal_parameters); - - foreach_list_const(node, &sig->body) { - const ir_instruction *const original = (ir_instruction *) node; - - ir_instruction *copy = original->clone(linked, ht); - linked_sig->body.push_tail(copy); - } - - linked_sig->is_defined = true; - hash_table_dtor(ht); - - /* Patch references inside the function to things outside the function - * (i.e., function calls and global variables). - */ - linked_sig->accept(this); - - ir->set_callee(linked_sig); - - return visit_continue; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (hash_table_find(locals, ir->var) == NULL) { - /* The non-function variable must be a global, so try to find the - * variable in the shader's symbol table. If the variable is not - * found, then it's a global that *MUST* be defined in the original - * shader. - */ - ir_variable *var = linked->symbols->get_variable(ir->var->name); - if (var == NULL) { - /* Clone the ir_variable that the dereference already has and add - * it to the linked shader. - */ - var = ir->var->clone(linked, NULL); - linked->symbols->add_variable(var); - linked->ir->push_head(var); - } else if (var->type->is_array()) { - /* It is possible to have a global array declared in multiple - * shaders without a size. The array is implicitly sized by the - * maximal access to it in *any* shader. Because of this, we - * need to track the maximal access to the array as linking pulls - * more functions in that access the array. - */ - var->max_array_access = - MAX2(var->max_array_access, ir->var->max_array_access); - - if (var->type->length == 0 && ir->var->type->length != 0) - var->type = ir->var->type; - } - - ir->var = var; - } - - return visit_continue; - } - - /** Was function linking successful? */ - bool success; - -private: - /** - * Shader program being linked - * - * This is only used for logging error messages. - */ - gl_shader_program *prog; - - /** List of shaders available for linking. */ - gl_shader **shader_list; - - /** Number of shaders available for linking. */ - unsigned num_shaders; - - /** - * Final linked shader - * - * This is used two ways. It is used to find global variables in the - * linked shader that are accessed by the function. It is also used to add - * global variables from the shader where the function originated. - */ - gl_shader *linked; - - /** - * Table of variables local to the function. - */ - hash_table *locals; -}; - - -/** - * Searches a list of shaders for a particular function definition - */ -ir_function_signature * -find_matching_signature(const char *name, const exec_list *actual_parameters, - gl_shader **shader_list, unsigned num_shaders, - bool use_builtin) -{ - for (unsigned i = 0; i < num_shaders; i++) { - ir_function *const f = shader_list[i]->symbols->get_function(name); - - if (f == NULL) - continue; - - ir_function_signature *sig = f->matching_signature(actual_parameters); - - if ((sig == NULL) || !sig->is_defined) - continue; - - /* If this function expects to bind to a built-in function and the - * signature that we found isn't a built-in, keep looking. Also keep - * looking if we expect a non-built-in but found a built-in. - */ - if (use_builtin != sig->is_builtin) - continue; - - return sig; - } - - return NULL; -} - - -bool -link_function_calls(gl_shader_program *prog, gl_shader *main, - gl_shader **shader_list, unsigned num_shaders) -{ - call_link_visitor v(prog, main, shader_list, num_shaders); - - v.run(main->ir); - return v.success; -} +/* + * 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 "main/core.h" +#include "glsl_symbol_table.h" +#include "glsl_parser_extras.h" +#include "ir.h" +#include "program.h" +#include "program/hash_table.h" +#include "linker.h" + +static ir_function_signature * +find_matching_signature(const char *name, const exec_list *actual_parameters, + gl_shader **shader_list, unsigned num_shaders, + bool use_builtin); + +class call_link_visitor : public ir_hierarchical_visitor { +public: + call_link_visitor(gl_shader_program *prog, gl_shader *linked, + gl_shader **shader_list, unsigned num_shaders) + { + this->prog = prog; + this->shader_list = shader_list; + this->num_shaders = num_shaders; + this->success = true; + this->linked = linked; + + this->locals = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + } + + ~call_link_visitor() + { + hash_table_dtor(this->locals); + } + + virtual ir_visitor_status visit(ir_variable *ir) + { + hash_table_insert(locals, ir, ir); + return visit_continue; + } + + virtual ir_visitor_status visit_enter(ir_call *ir) + { + /* If ir is an ir_call from a function that was imported from another + * shader callee will point to an ir_function_signature in the original + * shader. In this case the function signature MUST NOT BE MODIFIED. + * Doing so will modify the original shader. This may prevent that + * shader from being linkable in other programs. + */ + const ir_function_signature *const callee = ir->get_callee(); + assert(callee != NULL); + const char *const name = callee->function_name(); + + /* Determine if the requested function signature already exists in the + * final linked shader. If it does, use it as the target of the call. + */ + ir_function_signature *sig = + find_matching_signature(name, &callee->parameters, &linked, 1, + ir->use_builtin); + if (sig != NULL) { + ir->set_callee(sig); + return visit_continue; + } + + /* Try to find the signature in one of the other shaders that is being + * linked. If it's not found there, return an error. + */ + sig = find_matching_signature(name, &ir->actual_parameters, shader_list, + num_shaders, ir->use_builtin); + if (sig == NULL) { + /* FINISHME: Log the full signature of unresolved function. + */ + linker_error(this->prog, "unresolved reference to function `%s'\n", + name); + this->success = false; + return visit_stop; + } + + /* Find the prototype information in the linked shader. Generate any + * details that may be missing. + */ + ir_function *f = linked->symbols->get_function(name); + if (f == NULL) { + f = new(linked) ir_function(name); + + /* Add the new function to the linked IR. Put it at the end + * so that it comes after any global variable declarations + * that it refers to. + */ + linked->symbols->add_function(f); + linked->ir->push_tail(f); + } + + ir_function_signature *linked_sig = + f->exact_matching_signature(&callee->parameters); + if ((linked_sig == NULL) + || ((linked_sig != NULL) + && (linked_sig->is_builtin != ir->use_builtin))) { + linked_sig = new(linked) ir_function_signature(callee->return_type); + f->add_signature(linked_sig); + } + + /* At this point linked_sig and called may be the same. If ir is an + * ir_call from linked then linked_sig and callee will be + * ir_function_signatures that have no definitions (is_defined is false). + */ + assert(!linked_sig->is_defined); + assert(linked_sig->body.is_empty()); + + /* Create an in-place clone of the function definition. This multistep + * process introduces some complexity here, but it has some advantages. + * The parameter list and the and function body are cloned separately. + * The clone of the parameter list is used to prime the hashtable used + * to replace variable references in the cloned body. + * + * The big advantage is that the ir_function_signature does not change. + * This means that we don't have to process the rest of the IR tree to + * patch ir_call nodes. In addition, there is no way to remove or + * replace signature stored in a function. One could easily be added, + * but this avoids the need. + */ + struct hash_table *ht = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + exec_list formal_parameters; + foreach_list_const(node, &sig->parameters) { + const ir_instruction *const original = (ir_instruction *) node; + assert(const_cast(original)->as_variable()); + + ir_instruction *copy = original->clone(linked, ht); + formal_parameters.push_tail(copy); + } + + linked_sig->replace_parameters(&formal_parameters); + + foreach_list_const(node, &sig->body) { + const ir_instruction *const original = (ir_instruction *) node; + + ir_instruction *copy = original->clone(linked, ht); + linked_sig->body.push_tail(copy); + } + + linked_sig->is_defined = true; + hash_table_dtor(ht); + + /* Patch references inside the function to things outside the function + * (i.e., function calls and global variables). + */ + linked_sig->accept(this); + + ir->set_callee(linked_sig); + + return visit_continue; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + if (hash_table_find(locals, ir->var) == NULL) { + /* The non-function variable must be a global, so try to find the + * variable in the shader's symbol table. If the variable is not + * found, then it's a global that *MUST* be defined in the original + * shader. + */ + ir_variable *var = linked->symbols->get_variable(ir->var->name); + if (var == NULL) { + /* Clone the ir_variable that the dereference already has and add + * it to the linked shader. + */ + var = ir->var->clone(linked, NULL); + linked->symbols->add_variable(var); + linked->ir->push_head(var); + } else if (var->type->is_array()) { + /* It is possible to have a global array declared in multiple + * shaders without a size. The array is implicitly sized by the + * maximal access to it in *any* shader. Because of this, we + * need to track the maximal access to the array as linking pulls + * more functions in that access the array. + */ + var->max_array_access = + MAX2(var->max_array_access, ir->var->max_array_access); + + if (var->type->length == 0 && ir->var->type->length != 0) + var->type = ir->var->type; + } + + ir->var = var; + } + + return visit_continue; + } + + /** Was function linking successful? */ + bool success; + +private: + /** + * Shader program being linked + * + * This is only used for logging error messages. + */ + gl_shader_program *prog; + + /** List of shaders available for linking. */ + gl_shader **shader_list; + + /** Number of shaders available for linking. */ + unsigned num_shaders; + + /** + * Final linked shader + * + * This is used two ways. It is used to find global variables in the + * linked shader that are accessed by the function. It is also used to add + * global variables from the shader where the function originated. + */ + gl_shader *linked; + + /** + * Table of variables local to the function. + */ + hash_table *locals; +}; + + +/** + * Searches a list of shaders for a particular function definition + */ +ir_function_signature * +find_matching_signature(const char *name, const exec_list *actual_parameters, + gl_shader **shader_list, unsigned num_shaders, + bool use_builtin) +{ + for (unsigned i = 0; i < num_shaders; i++) { + ir_function *const f = shader_list[i]->symbols->get_function(name); + + if (f == NULL) + continue; + + ir_function_signature *sig = f->matching_signature(actual_parameters); + + if ((sig == NULL) || !sig->is_defined) + continue; + + /* If this function expects to bind to a built-in function and the + * signature that we found isn't a built-in, keep looking. Also keep + * looking if we expect a non-built-in but found a built-in. + */ + if (use_builtin != sig->is_builtin) + continue; + + return sig; + } + + return NULL; +} + + +bool +link_function_calls(gl_shader_program *prog, gl_shader *main, + gl_shader **shader_list, unsigned num_shaders) +{ + call_link_visitor v(prog, main, shader_list, num_shaders); + + v.run(main->ir); + return v.success; +} diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index ad50fe18d..195f58f29 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1,1831 +1,1831 @@ -/* - * 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. - */ - -/** - * \file linker.cpp - * GLSL linker implementation - * - * Given a set of shaders that are to be linked to generate a final program, - * there are three distinct stages. - * - * In the first stage shaders are partitioned into groups based on the shader - * type. All shaders of a particular type (e.g., vertex shaders) are linked - * together. - * - * - Undefined references in each shader are resolve to definitions in - * another shader. - * - Types and qualifiers of uniforms, outputs, and global variables defined - * in multiple shaders with the same name are verified to be the same. - * - Initializers for uniforms and global variables defined - * in multiple shaders with the same name are verified to be the same. - * - * The result, in the terminology of the GLSL spec, is a set of shader - * executables for each processing unit. - * - * After the first stage is complete, a series of semantic checks are performed - * on each of the shader executables. - * - * - Each shader executable must define a \c main function. - * - Each vertex shader executable must write to \c gl_Position. - * - Each fragment shader executable must write to either \c gl_FragData or - * \c gl_FragColor. - * - * In the final stage individual shader executables are linked to create a - * complete exectuable. - * - * - Types of uniforms defined in multiple shader stages with the same name - * are verified to be the same. - * - Initializers for uniforms defined in multiple shader stages with the - * same name are verified to be the same. - * - Types and qualifiers of outputs defined in one stage are verified to - * be the same as the types and qualifiers of inputs defined with the same - * name in a later stage. - * - * \author Ian Romanick - */ - -#include "main/core.h" -#include "glsl_symbol_table.h" -#include "ir.h" -#include "program.h" -#include "program/hash_table.h" -#include "linker.h" -#include "ir_optimization.h" - -extern "C" { -#include "main/shaderobj.h" -} - -/** - * Visitor that determines whether or not a variable is ever written. - */ -class find_assignment_visitor : public ir_hierarchical_visitor { -public: - find_assignment_visitor(const char *name) - : name(name), found(false) - { - /* empty */ - } - - virtual ir_visitor_status visit_enter(ir_assignment *ir) - { - ir_variable *const var = ir->lhs->variable_referenced(); - - if (strcmp(name, var->name) == 0) { - found = true; - return visit_stop; - } - - return visit_continue_with_parent; - } - - virtual ir_visitor_status visit_enter(ir_call *ir) - { - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *sig_param = (ir_variable *)sig_iter.get(); - - if (sig_param->mode == ir_var_out || - sig_param->mode == ir_var_inout) { - ir_variable *var = param_rval->variable_referenced(); - if (var && strcmp(name, var->name) == 0) { - found = true; - return visit_stop; - } - } - sig_iter.next(); - } - - return visit_continue_with_parent; - } - - bool variable_found() - { - return found; - } - -private: - const char *name; /**< Find writes to a variable with this name. */ - bool found; /**< Was a write to the variable found? */ -}; - - -/** - * Visitor that determines whether or not a variable is ever read. - */ -class find_deref_visitor : public ir_hierarchical_visitor { -public: - find_deref_visitor(const char *name) - : name(name), found(false) - { - /* empty */ - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (strcmp(this->name, ir->var->name) == 0) { - this->found = true; - return visit_stop; - } - - return visit_continue; - } - - bool variable_found() const - { - return this->found; - } - -private: - const char *name; /**< Find writes to a variable with this name. */ - bool found; /**< Was a write to the variable found? */ -}; - - -void -linker_error(gl_shader_program *prog, const char *fmt, ...) -{ - va_list ap; - - ralloc_strcat(&prog->InfoLog, "error: "); - va_start(ap, fmt); - ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); - va_end(ap); - - prog->LinkStatus = false; -} - - -void -linker_warning(gl_shader_program *prog, const char *fmt, ...) -{ - va_list ap; - - ralloc_strcat(&prog->InfoLog, "error: "); - va_start(ap, fmt); - ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); - va_end(ap); - -} - - -void -invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, - int generic_base) -{ - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != (unsigned) mode)) - continue; - - /* Only assign locations for generic attributes / varyings / etc. - */ - if ((var->location >= generic_base) && !var->explicit_location) - var->location = -1; - } -} - - -/** - * Determine the number of attribute slots required for a particular type - * - * This code is here because it implements the language rules of a specific - * GLSL version. Since it's a property of the language and not a property of - * types in general, it doesn't really belong in glsl_type. - */ -unsigned -count_attribute_slots(const glsl_type *t) -{ - /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: - * - * "A scalar input counts the same amount against this limit as a vec4, - * so applications may want to consider packing groups of four - * unrelated float inputs together into a vector to better utilize the - * capabilities of the underlying hardware. A matrix input will use up - * multiple locations. The number of locations used will equal the - * number of columns in the matrix." - * - * The spec does not explicitly say how arrays are counted. However, it - * should be safe to assume the total number of slots consumed by an array - * is the number of entries in the array multiplied by the number of slots - * consumed by a single element of the array. - */ - - if (t->is_array()) - return t->array_size() * count_attribute_slots(t->element_type()); - - if (t->is_matrix()) - return t->matrix_columns; - - return 1; -} - - -/** - * Verify that a vertex shader executable meets all semantic requirements - * - * \param shader Vertex shader executable to be verified - */ -bool -validate_vertex_shader_executable(struct gl_shader_program *prog, - struct gl_shader *shader) -{ - if (shader == NULL) - return true; - - find_assignment_visitor find("gl_Position"); - find.run(shader->ir); - if (!find.variable_found()) { - linker_error(prog, "vertex shader does not write to `gl_Position'\n"); - return false; - } - - if (prog->Version >= 130) { - /* From section 7.1 (Vertex Shader Special Variables) of the - * GLSL 1.30 spec: - * - * "It is an error for a shader to statically write both - * gl_ClipVertex and gl_ClipDistance." - */ - find_assignment_visitor clip_vertex("gl_ClipVertex"); - find_assignment_visitor clip_distance("gl_ClipDistance"); - - clip_vertex.run(shader->ir); - clip_distance.run(shader->ir); - if (clip_vertex.variable_found() && clip_distance.variable_found()) { - linker_error(prog, "vertex shader writes to both `gl_ClipVertex' " - "and `gl_ClipDistance'\n"); - return false; - } - } - - return true; -} - - -/** - * Verify that a fragment shader executable meets all semantic requirements - * - * \param shader Fragment shader executable to be verified - */ -bool -validate_fragment_shader_executable(struct gl_shader_program *prog, - struct gl_shader *shader) -{ - if (shader == NULL) - return true; - - find_assignment_visitor frag_color("gl_FragColor"); - find_assignment_visitor frag_data("gl_FragData"); - - frag_color.run(shader->ir); - frag_data.run(shader->ir); - - if (frag_color.variable_found() && frag_data.variable_found()) { - linker_error(prog, "fragment shader writes to both " - "`gl_FragColor' and `gl_FragData'\n"); - return false; - } - - return true; -} - - -/** - * Generate a string describing the mode of a variable - */ -static const char * -mode_string(const ir_variable *var) -{ - switch (var->mode) { - case ir_var_auto: - return (var->read_only) ? "global constant" : "global variable"; - - case ir_var_uniform: return "uniform"; - case ir_var_in: return "shader input"; - case ir_var_out: return "shader output"; - case ir_var_inout: return "shader inout"; - - case ir_var_const_in: - case ir_var_temporary: - default: - assert(!"Should not get here."); - return "invalid variable"; - } -} - - -/** - * Perform validation of global variables used across multiple shaders - */ -bool -cross_validate_globals(struct gl_shader_program *prog, - struct gl_shader **shader_list, - unsigned num_shaders, - bool uniforms_only) -{ - /* Examine all of the uniforms in all of the shaders and cross validate - * them. - */ - glsl_symbol_table variables; - for (unsigned i = 0; i < num_shaders; i++) { - if (shader_list[i] == NULL) - continue; - - foreach_list(node, shader_list[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if (var == NULL) - continue; - - if (uniforms_only && (var->mode != ir_var_uniform)) - continue; - - /* Don't cross validate temporaries that are at global scope. These - * will eventually get pulled into the shaders 'main'. - */ - if (var->mode == ir_var_temporary) - continue; - - /* If a global with this name has already been seen, verify that the - * new instance has the same type. In addition, if the globals have - * initializers, the values of the initializers must be the same. - */ - ir_variable *const existing = variables.get_variable(var->name); - if (existing != NULL) { - if (var->type != existing->type) { - /* Consider the types to be "the same" if both types are arrays - * of the same type and one of the arrays is implicitly sized. - * In addition, set the type of the linked variable to the - * explicitly sized array. - */ - if (var->type->is_array() - && existing->type->is_array() - && (var->type->fields.array == existing->type->fields.array) - && ((var->type->length == 0) - || (existing->type->length == 0))) { - if (var->type->length != 0) { - existing->type = var->type; - } - } else { - linker_error(prog, "%s `%s' declared as type " - "`%s' and type `%s'\n", - mode_string(var), - var->name, var->type->name, - existing->type->name); - return false; - } - } - - if (var->explicit_location) { - if (existing->explicit_location - && (var->location != existing->location)) { - linker_error(prog, "explicit locations for %s " - "`%s' have differing values\n", - mode_string(var), var->name); - return false; - } - - existing->location = var->location; - existing->explicit_location = true; - } - - /* Validate layout qualifiers for gl_FragDepth. - * - * From the AMD/ARB_conservative_depth specs: - * "If gl_FragDepth is redeclared in any fragment shader in - * a program, it must be redeclared in all fragment shaders in that - * program that have static assignments to gl_FragDepth. All - * redeclarations of gl_FragDepth in all fragment shaders in - * a single program must have the same set of qualifiers." - */ - if (strcmp(var->name, "gl_FragDepth") == 0) { - bool layout_declared = var->depth_layout != ir_depth_layout_none; - bool layout_differs = var->depth_layout != existing->depth_layout; - if (layout_declared && layout_differs) { - linker_error(prog, - "All redeclarations of gl_FragDepth in all fragment shaders " - "in a single program must have the same set of qualifiers."); - } - if (var->used && layout_differs) { - linker_error(prog, - "If gl_FragDepth is redeclared with a layout qualifier in" - "any fragment shader, it must be redeclared with the same" - "layout qualifier in all fragment shaders that have" - "assignments to gl_FragDepth"); - } - } - - /* FINISHME: Handle non-constant initializers. - */ - if (var->constant_value != NULL) { - if (existing->constant_value != NULL) { - if (!var->constant_value->has_value(existing->constant_value)) { - linker_error(prog, "initializers for %s " - "`%s' have differing values\n", - mode_string(var), var->name); - return false; - } - } else - /* If the first-seen instance of a particular uniform did not - * have an initializer but a later instance does, copy the - * initializer to the version stored in the symbol table. - */ - /* FINISHME: This is wrong. The constant_value field should - * FINISHME: not be modified! Imagine a case where a shader - * FINISHME: without an initializer is linked in two different - * FINISHME: programs with shaders that have differing - * FINISHME: initializers. Linking with the first will - * FINISHME: modify the shader, and linking with the second - * FINISHME: will fail. - */ - existing->constant_value = - var->constant_value->clone(ralloc_parent(existing), NULL); - } - - if (existing->invariant != var->invariant) { - linker_error(prog, "declarations for %s `%s' have " - "mismatching invariant qualifiers\n", - mode_string(var), var->name); - return false; - } - if (existing->centroid != var->centroid) { - linker_error(prog, "declarations for %s `%s' have " - "mismatching centroid qualifiers\n", - mode_string(var), var->name); - return false; - } - } else - variables.add_variable(var); - } - } - - return true; -} - - -/** - * Perform validation of uniforms used across multiple shader stages - */ -bool -cross_validate_uniforms(struct gl_shader_program *prog) -{ - return cross_validate_globals(prog, prog->_LinkedShaders, - MESA_SHADER_TYPES, true); -} - - -/** - * Validate that outputs from one stage match inputs of another - */ -bool -cross_validate_outputs_to_inputs(struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) -{ - glsl_symbol_table parameters; - /* FINISHME: Figure these out dynamically. */ - const char *const producer_stage = "vertex"; - const char *const consumer_stage = "fragment"; - - /* Find all shader outputs in the "producer" stage. - */ - foreach_list(node, producer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - /* FINISHME: For geometry shaders, this should also look for inout - * FINISHME: variables. - */ - if ((var == NULL) || (var->mode != ir_var_out)) - continue; - - parameters.add_variable(var); - } - - - /* Find all shader inputs in the "consumer" stage. Any variables that have - * matching outputs already in the symbol table must have the same type and - * qualifiers. - */ - foreach_list(node, consumer->ir) { - ir_variable *const input = ((ir_instruction *) node)->as_variable(); - - /* FINISHME: For geometry shaders, this should also look for inout - * FINISHME: variables. - */ - if ((input == NULL) || (input->mode != ir_var_in)) - continue; - - ir_variable *const output = parameters.get_variable(input->name); - if (output != NULL) { - /* Check that the types match between stages. - */ - if (input->type != output->type) { - /* There is a bit of a special case for gl_TexCoord. This - * built-in is unsized by default. Applications that variable - * access it must redeclare it with a size. There is some - * language in the GLSL spec that implies the fragment shader - * and vertex shader do not have to agree on this size. Other - * driver behave this way, and one or two applications seem to - * rely on it. - * - * Neither declaration needs to be modified here because the array - * sizes are fixed later when update_array_sizes is called. - * - * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec: - * - * "Unlike user-defined varying variables, the built-in - * varying variables don't have a strict one-to-one - * correspondence between the vertex language and the - * fragment language." - */ - if (!output->type->is_array() - || (strncmp("gl_", output->name, 3) != 0)) { - linker_error(prog, - "%s shader output `%s' declared as type `%s', " - "but %s shader input declared as type `%s'\n", - producer_stage, output->name, - output->type->name, - consumer_stage, input->type->name); - return false; - } - } - - /* Check that all of the qualifiers match between stages. - */ - if (input->centroid != output->centroid) { - linker_error(prog, - "%s shader output `%s' %s centroid qualifier, " - "but %s shader input %s centroid qualifier\n", - producer_stage, - output->name, - (output->centroid) ? "has" : "lacks", - consumer_stage, - (input->centroid) ? "has" : "lacks"); - return false; - } - - if (input->invariant != output->invariant) { - linker_error(prog, - "%s shader output `%s' %s invariant qualifier, " - "but %s shader input %s invariant qualifier\n", - producer_stage, - output->name, - (output->invariant) ? "has" : "lacks", - consumer_stage, - (input->invariant) ? "has" : "lacks"); - return false; - } - - if (input->interpolation != output->interpolation) { - linker_error(prog, - "%s shader output `%s' specifies %s " - "interpolation qualifier, " - "but %s shader input specifies %s " - "interpolation qualifier\n", - producer_stage, - output->name, - output->interpolation_string(), - consumer_stage, - input->interpolation_string()); - return false; - } - } - } - - return true; -} - - -/** - * Populates a shaders symbol table with all global declarations - */ -static void -populate_symbol_table(gl_shader *sh) -{ - sh->symbols = new(sh) glsl_symbol_table; - - foreach_list(node, sh->ir) { - ir_instruction *const inst = (ir_instruction *) node; - ir_variable *var; - ir_function *func; - - if ((func = inst->as_function()) != NULL) { - sh->symbols->add_function(func); - } else if ((var = inst->as_variable()) != NULL) { - sh->symbols->add_variable(var); - } - } -} - - -/** - * Remap variables referenced in an instruction tree - * - * This is used when instruction trees are cloned from one shader and placed in - * another. These trees will contain references to \c ir_variable nodes that - * do not exist in the target shader. This function finds these \c ir_variable - * references and replaces the references with matching variables in the target - * shader. - * - * If there is no matching variable in the target shader, a clone of the - * \c ir_variable is made and added to the target shader. The new variable is - * added to \b both the instruction stream and the symbol table. - * - * \param inst IR tree that is to be processed. - * \param symbols Symbol table containing global scope symbols in the - * linked shader. - * \param instructions Instruction stream where new variable declarations - * should be added. - */ -void -remap_variables(ir_instruction *inst, struct gl_shader *target, - hash_table *temps) -{ - class remap_visitor : public ir_hierarchical_visitor { - public: - remap_visitor(struct gl_shader *target, - hash_table *temps) - { - this->target = target; - this->symbols = target->symbols; - this->instructions = target->ir; - this->temps = temps; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (ir->var->mode == ir_var_temporary) { - ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var); - - assert(var != NULL); - ir->var = var; - return visit_continue; - } - - ir_variable *const existing = - this->symbols->get_variable(ir->var->name); - if (existing != NULL) - ir->var = existing; - else { - ir_variable *copy = ir->var->clone(this->target, NULL); - - this->symbols->add_variable(copy); - this->instructions->push_head(copy); - ir->var = copy; - } - - return visit_continue; - } - - private: - struct gl_shader *target; - glsl_symbol_table *symbols; - exec_list *instructions; - hash_table *temps; - }; - - remap_visitor v(target, temps); - - inst->accept(&v); -} - - -/** - * Move non-declarations from one instruction stream to another - * - * The intended usage pattern of this function is to pass the pointer to the - * head sentinel of a list (i.e., a pointer to the list cast to an \c exec_node - * pointer) for \c last and \c false for \c make_copies on the first - * call. Successive calls pass the return value of the previous call for - * \c last and \c true for \c make_copies. - * - * \param instructions Source instruction stream - * \param last Instruction after which new instructions should be - * inserted in the target instruction stream - * \param make_copies Flag selecting whether instructions in \c instructions - * should be copied (via \c ir_instruction::clone) into the - * target list or moved. - * - * \return - * The new "last" instruction in the target instruction stream. This pointer - * is suitable for use as the \c last parameter of a later call to this - * function. - */ -exec_node * -move_non_declarations(exec_list *instructions, exec_node *last, - bool make_copies, gl_shader *target) -{ - hash_table *temps = NULL; - - if (make_copies) - temps = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - - foreach_list_safe(node, instructions) { - ir_instruction *inst = (ir_instruction *) node; - - if (inst->as_function()) - continue; - - ir_variable *var = inst->as_variable(); - if ((var != NULL) && (var->mode != ir_var_temporary)) - continue; - - assert(inst->as_assignment() - || ((var != NULL) && (var->mode == ir_var_temporary))); - - if (make_copies) { - inst = inst->clone(target, NULL); - - if (var != NULL) - hash_table_insert(temps, inst, var); - else - remap_variables(inst, target, temps); - } else { - inst->remove(); - } - - last->insert_after(inst); - last = inst; - } - - if (make_copies) - hash_table_dtor(temps); - - return last; -} - -/** - * Get the function signature for main from a shader - */ -static ir_function_signature * -get_main_function_signature(gl_shader *sh) -{ - ir_function *const f = sh->symbols->get_function("main"); - if (f != NULL) { - exec_list void_parameters; - - /* Look for the 'void main()' signature and ensure that it's defined. - * This keeps the linker from accidentally pick a shader that just - * contains a prototype for main. - * - * We don't have to check for multiple definitions of main (in multiple - * shaders) because that would have already been caught above. - */ - ir_function_signature *sig = f->matching_signature(&void_parameters); - if ((sig != NULL) && sig->is_defined) { - return sig; - } - } - - return NULL; -} - - -/** - * Combine a group of shaders for a single stage to generate a linked shader - * - * \note - * If this function is supplied a single shader, it is cloned, and the new - * shader is returned. - */ -static struct gl_shader * -link_intrastage_shaders(void *mem_ctx, - struct gl_context *ctx, - struct gl_shader_program *prog, - struct gl_shader **shader_list, - unsigned num_shaders) -{ - /* Check that global variables defined in multiple shaders are consistent. - */ - if (!cross_validate_globals(prog, shader_list, num_shaders, false)) - return NULL; - - /* Check that there is only a single definition of each function signature - * across all shaders. - */ - for (unsigned i = 0; i < (num_shaders - 1); i++) { - foreach_list(node, shader_list[i]->ir) { - ir_function *const f = ((ir_instruction *) node)->as_function(); - - if (f == NULL) - continue; - - for (unsigned j = i + 1; j < num_shaders; j++) { - ir_function *const other = - shader_list[j]->symbols->get_function(f->name); - - /* If the other shader has no function (and therefore no function - * signatures) with the same name, skip to the next shader. - */ - if (other == NULL) - continue; - - foreach_iter (exec_list_iterator, iter, *f) { - ir_function_signature *sig = - (ir_function_signature *) iter.get(); - - if (!sig->is_defined || sig->is_builtin) - continue; - - ir_function_signature *other_sig = - other->exact_matching_signature(& sig->parameters); - - if ((other_sig != NULL) && other_sig->is_defined - && !other_sig->is_builtin) { - linker_error(prog, "function `%s' is multiply defined", - f->name); - return NULL; - } - } - } - } - } - - /* Find the shader that defines main, and make a clone of it. - * - * Starting with the clone, search for undefined references. If one is - * found, find the shader that defines it. Clone the reference and add - * it to the shader. Repeat until there are no undefined references or - * until a reference cannot be resolved. - */ - gl_shader *main = NULL; - for (unsigned i = 0; i < num_shaders; i++) { - if (get_main_function_signature(shader_list[i]) != NULL) { - main = shader_list[i]; - break; - } - } - - if (main == NULL) { - linker_error(prog, "%s shader lacks `main'\n", - (shader_list[0]->Type == GL_VERTEX_SHADER) - ? "vertex" : "fragment"); - return NULL; - } - - gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type); - linked->ir = new(linked) exec_list; - clone_ir_list(mem_ctx, linked->ir, main->ir); - - populate_symbol_table(linked); - - /* The a pointer to the main function in the final linked shader (i.e., the - * copy of the original shader that contained the main function). - */ - ir_function_signature *const main_sig = get_main_function_signature(linked); - - /* Move any instructions other than variable declarations or function - * declarations into main. - */ - exec_node *insertion_point = - move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false, - linked); - - for (unsigned i = 0; i < num_shaders; i++) { - if (shader_list[i] == main) - continue; - - insertion_point = move_non_declarations(shader_list[i]->ir, - insertion_point, true, linked); - } - - /* Resolve initializers for global variables in the linked shader. - */ - unsigned num_linking_shaders = num_shaders; - for (unsigned i = 0; i < num_shaders; i++) - num_linking_shaders += shader_list[i]->num_builtins_to_link; - - gl_shader **linking_shaders = - (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *)); - - memcpy(linking_shaders, shader_list, - sizeof(linking_shaders[0]) * num_shaders); - - unsigned idx = num_shaders; - for (unsigned i = 0; i < num_shaders; i++) { - memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link, - sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link); - idx += shader_list[i]->num_builtins_to_link; - } - - assert(idx == num_linking_shaders); - - if (!link_function_calls(prog, linked, linking_shaders, - num_linking_shaders)) { - ctx->Driver.DeleteShader(ctx, linked); - linked = NULL; - } - - free(linking_shaders); - -#ifdef DEBUG - /* At this point linked should contain all of the linked IR, so - * validate it to make sure nothing went wrong. - */ - if (linked) - validate_ir_tree(linked->ir); -#endif - - /* Make a pass over all variable declarations to ensure that arrays with - * unspecified sizes have a size specified. The size is inferred from the - * max_array_access field. - */ - if (linked != NULL) { - class array_sizing_visitor : public ir_hierarchical_visitor { - public: - virtual ir_visitor_status visit(ir_variable *var) - { - if (var->type->is_array() && (var->type->length == 0)) { - const glsl_type *type = - glsl_type::get_array_instance(var->type->fields.array, - var->max_array_access + 1); - - assert(type != NULL); - var->type = type; - } - - return visit_continue; - } - } v; - - v.run(linked->ir); - } - - return linked; -} - - -struct uniform_node { - exec_node link; - struct gl_uniform *u; - unsigned slots; -}; - -/** - * Update the sizes of linked shader uniform arrays to the maximum - * array index used. - * - * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec: - * - * If one or more elements of an array are active, - * GetActiveUniform will return the name of the array in name, - * subject to the restrictions listed above. The type of the array - * is returned in type. The size parameter contains the highest - * array element index used, plus one. The compiler or linker - * determines the highest index used. There will be only one - * active uniform reported by the GL per uniform array. - - */ -static void -update_array_sizes(struct gl_shader_program *prog) -{ - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - foreach_list(node, prog->_LinkedShaders[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_uniform && - var->mode != ir_var_in && - var->mode != ir_var_out) || - !var->type->is_array()) - continue; - - unsigned int size = var->max_array_access; - for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) { - if (prog->_LinkedShaders[j] == NULL) - continue; - - foreach_list(node2, prog->_LinkedShaders[j]->ir) { - ir_variable *other_var = ((ir_instruction *) node2)->as_variable(); - if (!other_var) - continue; - - if (strcmp(var->name, other_var->name) == 0 && - other_var->max_array_access > size) { - size = other_var->max_array_access; - } - } - } - - if (size + 1 != var->type->fields.array->length) { - /* If this is a built-in uniform (i.e., it's backed by some - * fixed-function state), adjust the number of state slots to - * match the new array size. The number of slots per array entry - * is not known. It seems safe to assume that the total number of - * slots is an integer multiple of the number of array elements. - * Determine the number of slots per array element by dividing by - * the old (total) size. - */ - if (var->num_state_slots > 0) { - var->num_state_slots = (size + 1) - * (var->num_state_slots / var->type->length); - } - - var->type = glsl_type::get_array_instance(var->type->fields.array, - size + 1); - /* FINISHME: We should update the types of array - * dereferences of this variable now. - */ - } - } - } -} - -static void -add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht, - const char *name, const glsl_type *type, GLenum shader_type, - unsigned *next_shader_pos, unsigned *total_uniforms) -{ - if (type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - const glsl_type *field_type = type->fields.structure[i].type; - char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name, - type->fields.structure[i].name); - - add_uniform(mem_ctx, uniforms, ht, field_name, field_type, - shader_type, next_shader_pos, total_uniforms); - } - } else { - uniform_node *n = (uniform_node *) hash_table_find(ht, name); - unsigned int vec4_slots; - const glsl_type *array_elem_type = NULL; - - if (type->is_array()) { - array_elem_type = type->fields.array; - /* Array of structures. */ - if (array_elem_type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i); - add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type, - shader_type, next_shader_pos, total_uniforms); - } - return; - } - } - - /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out - * vectors to vec4 slots. - */ - if (type->is_array()) { - if (array_elem_type->is_sampler()) - vec4_slots = type->length; - else - vec4_slots = type->length * array_elem_type->matrix_columns; - } else if (type->is_sampler()) { - vec4_slots = 1; - } else { - vec4_slots = type->matrix_columns; - } - - if (n == NULL) { - n = (uniform_node *) calloc(1, sizeof(struct uniform_node)); - n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform)); - n->slots = vec4_slots; - - n->u->Name = strdup(name); - n->u->Type = type; - n->u->VertPos = -1; - n->u->FragPos = -1; - n->u->GeomPos = -1; - (*total_uniforms)++; - - hash_table_insert(ht, n, name); - uniforms->push_tail(& n->link); - } - - switch (shader_type) { - case GL_VERTEX_SHADER: - n->u->VertPos = *next_shader_pos; - break; - case GL_FRAGMENT_SHADER: - n->u->FragPos = *next_shader_pos; - break; - case GL_GEOMETRY_SHADER: - n->u->GeomPos = *next_shader_pos; - break; - } - - (*next_shader_pos) += vec4_slots; - } -} - -void -assign_uniform_locations(struct gl_shader_program *prog) -{ - /* */ - exec_list uniforms; - unsigned total_uniforms = 0; - hash_table *ht = hash_table_ctor(32, hash_table_string_hash, - hash_table_string_compare); - void *mem_ctx = ralloc_context(NULL); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - unsigned next_position = 0; - - foreach_list(node, prog->_LinkedShaders[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_uniform)) - continue; - - if (strncmp(var->name, "gl_", 3) == 0) { - /* At the moment, we don't allocate uniform locations for - * builtin uniforms. It's permitted by spec, and we'll - * likely switch to doing that at some point, but not yet. - */ - continue; - } - - var->location = next_position; - add_uniform(mem_ctx, &uniforms, ht, var->name, var->type, - prog->_LinkedShaders[i]->Type, - &next_position, &total_uniforms); - } - } - - ralloc_free(mem_ctx); - - gl_uniform_list *ul = (gl_uniform_list *) - calloc(1, sizeof(gl_uniform_list)); - - ul->Size = total_uniforms; - ul->NumUniforms = total_uniforms; - ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform)); - - unsigned idx = 0; - uniform_node *next; - for (uniform_node *node = (uniform_node *) uniforms.head - ; node->link.next != NULL - ; node = next) { - next = (uniform_node *) node->link.next; - - node->link.remove(); - memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform)); - idx++; - - free(node->u); - free(node); - } - - hash_table_dtor(ht); - - prog->Uniforms = ul; -} - - -/** - * Find a contiguous set of available bits in a bitmask. - * - * \param used_mask Bits representing used (1) and unused (0) locations - * \param needed_count Number of contiguous bits needed. - * - * \return - * Base location of the available bits on success or -1 on failure. - */ -int -find_available_slots(unsigned used_mask, unsigned needed_count) -{ - unsigned needed_mask = (1 << needed_count) - 1; - const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count; - - /* The comparison to 32 is redundant, but without it GCC emits "warning: - * cannot optimize possibly infinite loops" for the loop below. - */ - if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32)) - return -1; - - for (int i = 0; i <= max_bit_to_test; i++) { - if ((needed_mask & ~used_mask) == needed_mask) - return i; - - needed_mask <<= 1; - } - - return -1; -} - - -/** - * Assign locations for either VS inputs for FS outputs - * - * \param prog Shader program whose variables need locations assigned - * \param target_index Selector for the program target to receive location - * assignmnets. Must be either \c MESA_SHADER_VERTEX or - * \c MESA_SHADER_FRAGMENT. - * \param max_index Maximum number of generic locations. This corresponds - * to either the maximum number of draw buffers or the - * maximum number of generic attributes. - * - * \return - * If locations are successfully assigned, true is returned. Otherwise an - * error is emitted to the shader link log and false is returned. - * - * \bug - * Locations set via \c glBindFragDataLocation are not currently supported. - * Only locations assigned automatically by the linker, explicitly set by a - * layout qualifier, or explicitly set by a built-in variable (e.g., \c - * gl_FragColor) are supported for fragment shaders. - */ -bool -assign_attribute_or_color_locations(gl_shader_program *prog, - unsigned target_index, - unsigned max_index) -{ - /* Mark invalid locations as being used. - */ - unsigned used_locations = (max_index >= 32) - ? ~0 : ~((1 << max_index) - 1); - - assert((target_index == MESA_SHADER_VERTEX) - || (target_index == MESA_SHADER_FRAGMENT)); - - gl_shader *const sh = prog->_LinkedShaders[target_index]; - if (sh == NULL) - return true; - - /* Operate in a total of four passes. - * - * 1. Invalidate the location assignments for all vertex shader inputs. - * - * 2. Assign locations for inputs that have user-defined (via - * glBindVertexAttribLocation) locations. - * - * 3. Sort the attributes without assigned locations by number of slots - * required in decreasing order. Fragmentation caused by attribute - * locations assigned by the application may prevent large attributes - * from having enough contiguous space. - * - * 4. Assign locations to any inputs without assigned locations. - */ - - const int generic_base = (target_index == MESA_SHADER_VERTEX) - ? (int) VERT_ATTRIB_GENERIC0 : (int) FRAG_RESULT_DATA0; - - const enum ir_variable_mode direction = - (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out; - - - invalidate_variable_locations(sh, direction, generic_base); - - if ((target_index == MESA_SHADER_VERTEX) && (prog->Attributes != NULL)) { - for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) { - ir_variable *const var = - sh->symbols->get_variable(prog->Attributes->Parameters[i].Name); - - /* Note: attributes that occupy multiple slots, such as arrays or - * matrices, may appear in the attrib array multiple times. - */ - if ((var == NULL) || (var->location != -1)) - continue; - - /* From page 61 of the OpenGL 4.0 spec: - * - * "LinkProgram will fail if the attribute bindings assigned by - * BindAttribLocation do not leave not enough space to assign a - * location for an active matrix attribute or an active attribute - * array, both of which require multiple contiguous generic - * attributes." - * - * Previous versions of the spec contain similar language but omit the - * bit about attribute arrays. - * - * Page 61 of the OpenGL 4.0 spec also says: - * - * "It is possible for an application to bind more than one - * attribute name to the same location. This is referred to as - * aliasing. This will only work if only one of the aliased - * attributes is active in the executable program, or if no path - * through the shader consumes more than one attribute of a set - * of attributes aliased to the same location. A link error can - * occur if the linker determines that every path through the - * shader consumes multiple aliased attributes, but - * implementations are not required to generate an error in this - * case." - * - * These two paragraphs are either somewhat contradictory, or I don't - * fully understand one or both of them. - */ - /* FINISHME: The code as currently written does not support attribute - * FINISHME: location aliasing (see comment above). - */ - const int attr = prog->Attributes->Parameters[i].StateIndexes[0]; - const unsigned slots = count_attribute_slots(var->type); - - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << slots) - 1; - - /* Generate a link error if the set of bits requested for this - * attribute overlaps any previously allocated bits. - */ - if ((~(use_mask << attr) & used_locations) != used_locations) { - linker_error(prog, - "insufficient contiguous attribute locations " - "available for vertex shader input `%s'", - var->name); - return false; - } - - var->location = VERT_ATTRIB_GENERIC0 + attr; - used_locations |= (use_mask << attr); - } - } - - /* Temporary storage for the set of attributes that need locations assigned. - */ - struct temp_attr { - unsigned slots; - ir_variable *var; - - /* Used below in the call to qsort. */ - static int compare(const void *a, const void *b) - { - const temp_attr *const l = (const temp_attr *) a; - const temp_attr *const r = (const temp_attr *) b; - - /* Reversed because we want a descending order sort below. */ - return r->slots - l->slots; - } - } to_assign[16]; - - unsigned num_attr = 0; - - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != (unsigned) direction)) - continue; - - if (var->explicit_location) { - const unsigned slots = count_attribute_slots(var->type); - const unsigned use_mask = (1 << slots) - 1; - const int attr = var->location - generic_base; - - if ((var->location >= (int)(max_index + generic_base)) - || (var->location < 0)) { - linker_error(prog, - "invalid explicit location %d specified for `%s'\n", - (var->location < 0) ? var->location : attr, - var->name); - return false; - } else if (var->location >= generic_base) { - used_locations |= (use_mask << attr); - } - } - - /* The location was explicitly assigned, nothing to do here. - */ - if (var->location != -1) - continue; - - to_assign[num_attr].slots = count_attribute_slots(var->type); - to_assign[num_attr].var = var; - num_attr++; - } - - /* If all of the attributes were assigned locations by the application (or - * are built-in attributes with fixed locations), return early. This should - * be the common case. - */ - if (num_attr == 0) - return true; - - qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare); - - if (target_index == MESA_SHADER_VERTEX) { - /* VERT_ATTRIB_GENERIC0 is a pseudo-alias for VERT_ATTRIB_POS. It can - * only be explicitly assigned by via glBindAttribLocation. Mark it as - * reserved to prevent it from being automatically allocated below. - */ - find_deref_visitor find("gl_Vertex"); - find.run(sh->ir); - if (find.variable_found()) - used_locations |= (1 << 0); - } - - for (unsigned i = 0; i < num_attr; i++) { - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << to_assign[i].slots) - 1; - - int location = find_available_slots(used_locations, to_assign[i].slots); - - if (location < 0) { - const char *const string = (target_index == MESA_SHADER_VERTEX) - ? "vertex shader input" : "fragment shader output"; - - linker_error(prog, - "insufficient contiguous attribute locations " - "available for %s `%s'", - string, to_assign[i].var->name); - return false; - } - - to_assign[i].var->location = generic_base + location; - used_locations |= (use_mask << location); - } - - return true; -} - - -/** - * Demote shader inputs and outputs that are not used in other stages - */ -void -demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) -{ - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != int(mode))) - continue; - - /* A shader 'in' or 'out' variable is only really an input or output if - * its value is used by other shader stages. This will cause the variable - * to have a location assigned. - */ - if (var->location == -1) { - var->mode = ir_var_auto; - } - } -} - - -bool -assign_varying_locations(struct gl_context *ctx, - struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) -{ - /* FINISHME: Set dynamically when geometry shader support is added. */ - unsigned output_index = VERT_RESULT_VAR0; - unsigned input_index = FRAG_ATTRIB_VAR0; - - /* Operate in a total of three passes. - * - * 1. Assign locations for any matching inputs and outputs. - * - * 2. Mark output variables in the producer that do not have locations as - * not being outputs. This lets the optimizer eliminate them. - * - * 3. Mark input variables in the consumer that do not have locations as - * not being inputs. This lets the optimizer eliminate them. - */ - - invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); - invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); - - foreach_list(node, producer->ir) { - ir_variable *const output_var = ((ir_instruction *) node)->as_variable(); - - if ((output_var == NULL) || (output_var->mode != ir_var_out) - || (output_var->location != -1)) - continue; - - ir_variable *const input_var = - consumer->symbols->get_variable(output_var->name); - - if ((input_var == NULL) || (input_var->mode != ir_var_in)) - continue; - - assert(input_var->location == -1); - - output_var->location = output_index; - input_var->location = input_index; - - /* FINISHME: Support for "varying" records in GLSL 1.50. */ - assert(!output_var->type->is_record()); - - if (output_var->type->is_array()) { - const unsigned slots = output_var->type->length - * output_var->type->fields.array->matrix_columns; - - output_index += slots; - input_index += slots; - } else { - const unsigned slots = output_var->type->matrix_columns; - - output_index += slots; - input_index += slots; - } - } - - unsigned varying_vectors = 0; - - foreach_list(node, consumer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_in)) - continue; - - if (var->location == -1) { - if (prog->Version <= 120) { - /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: - * - * Only those varying variables used (i.e. read) in - * the fragment shader executable must be written to - * by the vertex shader executable; declaring - * superfluous varying variables in a vertex shader is - * permissible. - * - * We interpret this text as meaning that the VS must - * write the variable for the FS to read it. See - * "glsl1-varying read but not written" in piglit. - */ - - linker_error(prog, "fragment shader varying %s not written " - "by vertex shader\n.", var->name); - } - - /* An 'in' variable is only really a shader input if its - * value is written by the previous stage. - */ - var->mode = ir_var_auto; - } else { - /* The packing rules are used for vertex shader inputs are also used - * for fragment shader inputs. - */ - varying_vectors += count_attribute_slots(var->type); - } - } - - if (ctx->API == API_OPENGLES2 || prog->Version == 100) { - if (varying_vectors > ctx->Const.MaxVarying) { - linker_error(prog, "shader uses too many varying vectors " - "(%u > %u)\n", - varying_vectors, ctx->Const.MaxVarying); - return false; - } - } else { - const unsigned float_components = varying_vectors * 4; - if (float_components > ctx->Const.MaxVarying * 4) { - linker_error(prog, "shader uses too many varying components " - "(%u > %u)\n", - float_components, ctx->Const.MaxVarying * 4); - return false; - } - } - - return true; -} - - -void -link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) -{ - void *mem_ctx = ralloc_context(NULL); // temporary linker context - - prog->LinkStatus = false; - prog->Validated = false; - prog->_Used = false; - - if (prog->InfoLog != NULL) - ralloc_free(prog->InfoLog); - - prog->InfoLog = ralloc_strdup(NULL, ""); - - /* Separate the shaders into groups based on their type. - */ - struct gl_shader **vert_shader_list; - unsigned num_vert_shaders = 0; - struct gl_shader **frag_shader_list; - unsigned num_frag_shaders = 0; - - vert_shader_list = (struct gl_shader **) - calloc(2 * prog->NumShaders, sizeof(struct gl_shader *)); - frag_shader_list = &vert_shader_list[prog->NumShaders]; - - unsigned min_version = UINT_MAX; - unsigned max_version = 0; - for (unsigned i = 0; i < prog->NumShaders; i++) { - min_version = MIN2(min_version, prog->Shaders[i]->Version); - max_version = MAX2(max_version, prog->Shaders[i]->Version); - - switch (prog->Shaders[i]->Type) { - case GL_VERTEX_SHADER: - vert_shader_list[num_vert_shaders] = prog->Shaders[i]; - num_vert_shaders++; - break; - case GL_FRAGMENT_SHADER: - frag_shader_list[num_frag_shaders] = prog->Shaders[i]; - num_frag_shaders++; - break; - case GL_GEOMETRY_SHADER: - /* FINISHME: Support geometry shaders. */ - assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER); - break; - } - } - - /* Previous to GLSL version 1.30, different compilation units could mix and - * match shading language versions. With GLSL 1.30 and later, the versions - * of all shaders must match. - */ - assert(min_version >= 100); - assert(max_version <= 130); - if ((max_version >= 130 || min_version == 100) - && min_version != max_version) { - linker_error(prog, "all shaders must use same shading " - "language version\n"); - goto done; - } - - prog->Version = max_version; - - for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] != NULL) - ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]); - - prog->_LinkedShaders[i] = NULL; - } - - /* Link all shaders for a particular stage and validate the result. - */ - if (num_vert_shaders > 0) { - gl_shader *const sh = - link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list, - num_vert_shaders); - - if (sh == NULL) - goto done; - - if (!validate_vertex_shader_executable(prog, sh)) - goto done; - - _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX], - sh); - } - - if (num_frag_shaders > 0) { - gl_shader *const sh = - link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list, - num_frag_shaders); - - if (sh == NULL) - goto done; - - if (!validate_fragment_shader_executable(prog, sh)) - goto done; - - _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT], - sh); - } - - /* Here begins the inter-stage linking phase. Some initial validation is - * performed, then locations are assigned for uniforms, attributes, and - * varyings. - */ - if (cross_validate_uniforms(prog)) { - unsigned prev; - - for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { - if (prog->_LinkedShaders[prev] != NULL) - break; - } - - /* Validate the inputs of each stage with the output of the preceding - * stage. - */ - for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - if (!cross_validate_outputs_to_inputs(prog, - prog->_LinkedShaders[prev], - prog->_LinkedShaders[i])) - goto done; - - prev = i; - } - - prog->LinkStatus = true; - } - - /* Do common optimization before assigning storage for attributes, - * uniforms, and varyings. Later optimization could possibly make - * some of that unused. - */ - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - detect_recursion_linked(prog, prog->_LinkedShaders[i]->ir); - if (!prog->LinkStatus) - goto done; - - while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) - ; - } - - update_array_sizes(prog); - - assign_uniform_locations(prog); - - /* FINISHME: The value of the max_attribute_index parameter is - * FINISHME: implementation dependent based on the value of - * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be - * FINISHME: at least 16, so hardcode 16 for now. - */ - if (!assign_attribute_or_color_locations(prog, MESA_SHADER_VERTEX, 16)) { - goto done; - } - - if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, ctx->Const.MaxDrawBuffers)) { - goto done; - } - - unsigned prev; - for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { - if (prog->_LinkedShaders[prev] != NULL) - break; - } - - for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - if (!assign_varying_locations(ctx, prog, - prog->_LinkedShaders[prev], - prog->_LinkedShaders[i])) { - goto done; - } - - prev = i; - } - - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { - demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], - ir_var_out); - } - - if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]; - - demote_shader_inputs_and_outputs(sh, ir_var_in); - demote_shader_inputs_and_outputs(sh, ir_var_inout); - demote_shader_inputs_and_outputs(sh, ir_var_out); - } - - if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]; - - demote_shader_inputs_and_outputs(sh, ir_var_in); - } - - /* OpenGL ES requires that a vertex shader and a fragment shader both be - * present in a linked program. By checking for use of shading language - * version 1.00, we also catch the GL_ARB_ES2_compatibility case. - */ - if (ctx->API == API_OPENGLES2 || prog->Version == 100) { - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) { - linker_error(prog, "program lacks a vertex shader\n"); - } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { - linker_error(prog, "program lacks a fragment shader\n"); - } - } - - /* FINISHME: Assign fragment shader output locations. */ - -done: - free(vert_shader_list); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - /* Retain any live IR, but trash the rest. */ - reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir); - } - - ralloc_free(mem_ctx); -} +/* + * 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. + */ + +/** + * \file linker.cpp + * GLSL linker implementation + * + * Given a set of shaders that are to be linked to generate a final program, + * there are three distinct stages. + * + * In the first stage shaders are partitioned into groups based on the shader + * type. All shaders of a particular type (e.g., vertex shaders) are linked + * together. + * + * - Undefined references in each shader are resolve to definitions in + * another shader. + * - Types and qualifiers of uniforms, outputs, and global variables defined + * in multiple shaders with the same name are verified to be the same. + * - Initializers for uniforms and global variables defined + * in multiple shaders with the same name are verified to be the same. + * + * The result, in the terminology of the GLSL spec, is a set of shader + * executables for each processing unit. + * + * After the first stage is complete, a series of semantic checks are performed + * on each of the shader executables. + * + * - Each shader executable must define a \c main function. + * - Each vertex shader executable must write to \c gl_Position. + * - Each fragment shader executable must write to either \c gl_FragData or + * \c gl_FragColor. + * + * In the final stage individual shader executables are linked to create a + * complete exectuable. + * + * - Types of uniforms defined in multiple shader stages with the same name + * are verified to be the same. + * - Initializers for uniforms defined in multiple shader stages with the + * same name are verified to be the same. + * - Types and qualifiers of outputs defined in one stage are verified to + * be the same as the types and qualifiers of inputs defined with the same + * name in a later stage. + * + * \author Ian Romanick + */ + +#include "main/core.h" +#include "glsl_symbol_table.h" +#include "ir.h" +#include "program.h" +#include "program/hash_table.h" +#include "linker.h" +#include "ir_optimization.h" + +extern "C" { +#include "main/shaderobj.h" +} + +/** + * Visitor that determines whether or not a variable is ever written. + */ +class find_assignment_visitor : public ir_hierarchical_visitor { +public: + find_assignment_visitor(const char *name) + : name(name), found(false) + { + /* empty */ + } + + virtual ir_visitor_status visit_enter(ir_assignment *ir) + { + ir_variable *const var = ir->lhs->variable_referenced(); + + if (strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } + + return visit_continue_with_parent; + } + + virtual ir_visitor_status visit_enter(ir_call *ir) + { + exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *sig_param = (ir_variable *)sig_iter.get(); + + if (sig_param->mode == ir_var_out || + sig_param->mode == ir_var_inout) { + ir_variable *var = param_rval->variable_referenced(); + if (var && strcmp(name, var->name) == 0) { + found = true; + return visit_stop; + } + } + sig_iter.next(); + } + + return visit_continue_with_parent; + } + + bool variable_found() + { + return found; + } + +private: + const char *name; /**< Find writes to a variable with this name. */ + bool found; /**< Was a write to the variable found? */ +}; + + +/** + * Visitor that determines whether or not a variable is ever read. + */ +class find_deref_visitor : public ir_hierarchical_visitor { +public: + find_deref_visitor(const char *name) + : name(name), found(false) + { + /* empty */ + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + if (strcmp(this->name, ir->var->name) == 0) { + this->found = true; + return visit_stop; + } + + return visit_continue; + } + + bool variable_found() const + { + return this->found; + } + +private: + const char *name; /**< Find writes to a variable with this name. */ + bool found; /**< Was a write to the variable found? */ +}; + + +void +linker_error(gl_shader_program *prog, const char *fmt, ...) +{ + va_list ap; + + ralloc_strcat(&prog->InfoLog, "error: "); + va_start(ap, fmt); + ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); + va_end(ap); + + prog->LinkStatus = false; +} + + +void +linker_warning(gl_shader_program *prog, const char *fmt, ...) +{ + va_list ap; + + ralloc_strcat(&prog->InfoLog, "error: "); + va_start(ap, fmt); + ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); + va_end(ap); + +} + + +void +invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, + int generic_base) +{ + foreach_list(node, sh->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != (unsigned) mode)) + continue; + + /* Only assign locations for generic attributes / varyings / etc. + */ + if ((var->location >= generic_base) && !var->explicit_location) + var->location = -1; + } +} + + +/** + * Determine the number of attribute slots required for a particular type + * + * This code is here because it implements the language rules of a specific + * GLSL version. Since it's a property of the language and not a property of + * types in general, it doesn't really belong in glsl_type. + */ +unsigned +count_attribute_slots(const glsl_type *t) +{ + /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: + * + * "A scalar input counts the same amount against this limit as a vec4, + * so applications may want to consider packing groups of four + * unrelated float inputs together into a vector to better utilize the + * capabilities of the underlying hardware. A matrix input will use up + * multiple locations. The number of locations used will equal the + * number of columns in the matrix." + * + * The spec does not explicitly say how arrays are counted. However, it + * should be safe to assume the total number of slots consumed by an array + * is the number of entries in the array multiplied by the number of slots + * consumed by a single element of the array. + */ + + if (t->is_array()) + return t->array_size() * count_attribute_slots(t->element_type()); + + if (t->is_matrix()) + return t->matrix_columns; + + return 1; +} + + +/** + * Verify that a vertex shader executable meets all semantic requirements + * + * \param shader Vertex shader executable to be verified + */ +bool +validate_vertex_shader_executable(struct gl_shader_program *prog, + struct gl_shader *shader) +{ + if (shader == NULL) + return true; + + find_assignment_visitor find("gl_Position"); + find.run(shader->ir); + if (!find.variable_found()) { + linker_error(prog, "vertex shader does not write to `gl_Position'\n"); + return false; + } + + if (prog->Version >= 130) { + /* From section 7.1 (Vertex Shader Special Variables) of the + * GLSL 1.30 spec: + * + * "It is an error for a shader to statically write both + * gl_ClipVertex and gl_ClipDistance." + */ + find_assignment_visitor clip_vertex("gl_ClipVertex"); + find_assignment_visitor clip_distance("gl_ClipDistance"); + + clip_vertex.run(shader->ir); + clip_distance.run(shader->ir); + if (clip_vertex.variable_found() && clip_distance.variable_found()) { + linker_error(prog, "vertex shader writes to both `gl_ClipVertex' " + "and `gl_ClipDistance'\n"); + return false; + } + } + + return true; +} + + +/** + * Verify that a fragment shader executable meets all semantic requirements + * + * \param shader Fragment shader executable to be verified + */ +bool +validate_fragment_shader_executable(struct gl_shader_program *prog, + struct gl_shader *shader) +{ + if (shader == NULL) + return true; + + find_assignment_visitor frag_color("gl_FragColor"); + find_assignment_visitor frag_data("gl_FragData"); + + frag_color.run(shader->ir); + frag_data.run(shader->ir); + + if (frag_color.variable_found() && frag_data.variable_found()) { + linker_error(prog, "fragment shader writes to both " + "`gl_FragColor' and `gl_FragData'\n"); + return false; + } + + return true; +} + + +/** + * Generate a string describing the mode of a variable + */ +static const char * +mode_string(const ir_variable *var) +{ + switch (var->mode) { + case ir_var_auto: + return (var->read_only) ? "global constant" : "global variable"; + + case ir_var_uniform: return "uniform"; + case ir_var_in: return "shader input"; + case ir_var_out: return "shader output"; + case ir_var_inout: return "shader inout"; + + case ir_var_const_in: + case ir_var_temporary: + default: + assert(!"Should not get here."); + return "invalid variable"; + } +} + + +/** + * Perform validation of global variables used across multiple shaders + */ +bool +cross_validate_globals(struct gl_shader_program *prog, + struct gl_shader **shader_list, + unsigned num_shaders, + bool uniforms_only) +{ + /* Examine all of the uniforms in all of the shaders and cross validate + * them. + */ + glsl_symbol_table variables; + for (unsigned i = 0; i < num_shaders; i++) { + if (shader_list[i] == NULL) + continue; + + foreach_list(node, shader_list[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if (var == NULL) + continue; + + if (uniforms_only && (var->mode != ir_var_uniform)) + continue; + + /* Don't cross validate temporaries that are at global scope. These + * will eventually get pulled into the shaders 'main'. + */ + if (var->mode == ir_var_temporary) + continue; + + /* If a global with this name has already been seen, verify that the + * new instance has the same type. In addition, if the globals have + * initializers, the values of the initializers must be the same. + */ + ir_variable *const existing = variables.get_variable(var->name); + if (existing != NULL) { + if (var->type != existing->type) { + /* Consider the types to be "the same" if both types are arrays + * of the same type and one of the arrays is implicitly sized. + * In addition, set the type of the linked variable to the + * explicitly sized array. + */ + if (var->type->is_array() + && existing->type->is_array() + && (var->type->fields.array == existing->type->fields.array) + && ((var->type->length == 0) + || (existing->type->length == 0))) { + if (var->type->length != 0) { + existing->type = var->type; + } + } else { + linker_error(prog, "%s `%s' declared as type " + "`%s' and type `%s'\n", + mode_string(var), + var->name, var->type->name, + existing->type->name); + return false; + } + } + + if (var->explicit_location) { + if (existing->explicit_location + && (var->location != existing->location)) { + linker_error(prog, "explicit locations for %s " + "`%s' have differing values\n", + mode_string(var), var->name); + return false; + } + + existing->location = var->location; + existing->explicit_location = true; + } + + /* Validate layout qualifiers for gl_FragDepth. + * + * From the AMD/ARB_conservative_depth specs: + * "If gl_FragDepth is redeclared in any fragment shader in + * a program, it must be redeclared in all fragment shaders in that + * program that have static assignments to gl_FragDepth. All + * redeclarations of gl_FragDepth in all fragment shaders in + * a single program must have the same set of qualifiers." + */ + if (strcmp(var->name, "gl_FragDepth") == 0) { + bool layout_declared = var->depth_layout != ir_depth_layout_none; + bool layout_differs = var->depth_layout != existing->depth_layout; + if (layout_declared && layout_differs) { + linker_error(prog, + "All redeclarations of gl_FragDepth in all fragment shaders " + "in a single program must have the same set of qualifiers."); + } + if (var->used && layout_differs) { + linker_error(prog, + "If gl_FragDepth is redeclared with a layout qualifier in" + "any fragment shader, it must be redeclared with the same" + "layout qualifier in all fragment shaders that have" + "assignments to gl_FragDepth"); + } + } + + /* FINISHME: Handle non-constant initializers. + */ + if (var->constant_value != NULL) { + if (existing->constant_value != NULL) { + if (!var->constant_value->has_value(existing->constant_value)) { + linker_error(prog, "initializers for %s " + "`%s' have differing values\n", + mode_string(var), var->name); + return false; + } + } else + /* If the first-seen instance of a particular uniform did not + * have an initializer but a later instance does, copy the + * initializer to the version stored in the symbol table. + */ + /* FINISHME: This is wrong. The constant_value field should + * FINISHME: not be modified! Imagine a case where a shader + * FINISHME: without an initializer is linked in two different + * FINISHME: programs with shaders that have differing + * FINISHME: initializers. Linking with the first will + * FINISHME: modify the shader, and linking with the second + * FINISHME: will fail. + */ + existing->constant_value = + var->constant_value->clone(ralloc_parent(existing), NULL); + } + + if (existing->invariant != var->invariant) { + linker_error(prog, "declarations for %s `%s' have " + "mismatching invariant qualifiers\n", + mode_string(var), var->name); + return false; + } + if (existing->centroid != var->centroid) { + linker_error(prog, "declarations for %s `%s' have " + "mismatching centroid qualifiers\n", + mode_string(var), var->name); + return false; + } + } else + variables.add_variable(var); + } + } + + return true; +} + + +/** + * Perform validation of uniforms used across multiple shader stages + */ +bool +cross_validate_uniforms(struct gl_shader_program *prog) +{ + return cross_validate_globals(prog, prog->_LinkedShaders, + MESA_SHADER_TYPES, true); +} + + +/** + * Validate that outputs from one stage match inputs of another + */ +bool +cross_validate_outputs_to_inputs(struct gl_shader_program *prog, + gl_shader *producer, gl_shader *consumer) +{ + glsl_symbol_table parameters; + /* FINISHME: Figure these out dynamically. */ + const char *const producer_stage = "vertex"; + const char *const consumer_stage = "fragment"; + + /* Find all shader outputs in the "producer" stage. + */ + foreach_list(node, producer->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + /* FINISHME: For geometry shaders, this should also look for inout + * FINISHME: variables. + */ + if ((var == NULL) || (var->mode != ir_var_out)) + continue; + + parameters.add_variable(var); + } + + + /* Find all shader inputs in the "consumer" stage. Any variables that have + * matching outputs already in the symbol table must have the same type and + * qualifiers. + */ + foreach_list(node, consumer->ir) { + ir_variable *const input = ((ir_instruction *) node)->as_variable(); + + /* FINISHME: For geometry shaders, this should also look for inout + * FINISHME: variables. + */ + if ((input == NULL) || (input->mode != ir_var_in)) + continue; + + ir_variable *const output = parameters.get_variable(input->name); + if (output != NULL) { + /* Check that the types match between stages. + */ + if (input->type != output->type) { + /* There is a bit of a special case for gl_TexCoord. This + * built-in is unsized by default. Applications that variable + * access it must redeclare it with a size. There is some + * language in the GLSL spec that implies the fragment shader + * and vertex shader do not have to agree on this size. Other + * driver behave this way, and one or two applications seem to + * rely on it. + * + * Neither declaration needs to be modified here because the array + * sizes are fixed later when update_array_sizes is called. + * + * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec: + * + * "Unlike user-defined varying variables, the built-in + * varying variables don't have a strict one-to-one + * correspondence between the vertex language and the + * fragment language." + */ + if (!output->type->is_array() + || (strncmp("gl_", output->name, 3) != 0)) { + linker_error(prog, + "%s shader output `%s' declared as type `%s', " + "but %s shader input declared as type `%s'\n", + producer_stage, output->name, + output->type->name, + consumer_stage, input->type->name); + return false; + } + } + + /* Check that all of the qualifiers match between stages. + */ + if (input->centroid != output->centroid) { + linker_error(prog, + "%s shader output `%s' %s centroid qualifier, " + "but %s shader input %s centroid qualifier\n", + producer_stage, + output->name, + (output->centroid) ? "has" : "lacks", + consumer_stage, + (input->centroid) ? "has" : "lacks"); + return false; + } + + if (input->invariant != output->invariant) { + linker_error(prog, + "%s shader output `%s' %s invariant qualifier, " + "but %s shader input %s invariant qualifier\n", + producer_stage, + output->name, + (output->invariant) ? "has" : "lacks", + consumer_stage, + (input->invariant) ? "has" : "lacks"); + return false; + } + + if (input->interpolation != output->interpolation) { + linker_error(prog, + "%s shader output `%s' specifies %s " + "interpolation qualifier, " + "but %s shader input specifies %s " + "interpolation qualifier\n", + producer_stage, + output->name, + output->interpolation_string(), + consumer_stage, + input->interpolation_string()); + return false; + } + } + } + + return true; +} + + +/** + * Populates a shaders symbol table with all global declarations + */ +static void +populate_symbol_table(gl_shader *sh) +{ + sh->symbols = new(sh) glsl_symbol_table; + + foreach_list(node, sh->ir) { + ir_instruction *const inst = (ir_instruction *) node; + ir_variable *var; + ir_function *func; + + if ((func = inst->as_function()) != NULL) { + sh->symbols->add_function(func); + } else if ((var = inst->as_variable()) != NULL) { + sh->symbols->add_variable(var); + } + } +} + + +/** + * Remap variables referenced in an instruction tree + * + * This is used when instruction trees are cloned from one shader and placed in + * another. These trees will contain references to \c ir_variable nodes that + * do not exist in the target shader. This function finds these \c ir_variable + * references and replaces the references with matching variables in the target + * shader. + * + * If there is no matching variable in the target shader, a clone of the + * \c ir_variable is made and added to the target shader. The new variable is + * added to \b both the instruction stream and the symbol table. + * + * \param inst IR tree that is to be processed. + * \param symbols Symbol table containing global scope symbols in the + * linked shader. + * \param instructions Instruction stream where new variable declarations + * should be added. + */ +void +remap_variables(ir_instruction *inst, struct gl_shader *target, + hash_table *temps) +{ + class remap_visitor : public ir_hierarchical_visitor { + public: + remap_visitor(struct gl_shader *target, + hash_table *temps) + { + this->target = target; + this->symbols = target->symbols; + this->instructions = target->ir; + this->temps = temps; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + if (ir->var->mode == ir_var_temporary) { + ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var); + + assert(var != NULL); + ir->var = var; + return visit_continue; + } + + ir_variable *const existing = + this->symbols->get_variable(ir->var->name); + if (existing != NULL) + ir->var = existing; + else { + ir_variable *copy = ir->var->clone(this->target, NULL); + + this->symbols->add_variable(copy); + this->instructions->push_head(copy); + ir->var = copy; + } + + return visit_continue; + } + + private: + struct gl_shader *target; + glsl_symbol_table *symbols; + exec_list *instructions; + hash_table *temps; + }; + + remap_visitor v(target, temps); + + inst->accept(&v); +} + + +/** + * Move non-declarations from one instruction stream to another + * + * The intended usage pattern of this function is to pass the pointer to the + * head sentinel of a list (i.e., a pointer to the list cast to an \c exec_node + * pointer) for \c last and \c false for \c make_copies on the first + * call. Successive calls pass the return value of the previous call for + * \c last and \c true for \c make_copies. + * + * \param instructions Source instruction stream + * \param last Instruction after which new instructions should be + * inserted in the target instruction stream + * \param make_copies Flag selecting whether instructions in \c instructions + * should be copied (via \c ir_instruction::clone) into the + * target list or moved. + * + * \return + * The new "last" instruction in the target instruction stream. This pointer + * is suitable for use as the \c last parameter of a later call to this + * function. + */ +exec_node * +move_non_declarations(exec_list *instructions, exec_node *last, + bool make_copies, gl_shader *target) +{ + hash_table *temps = NULL; + + if (make_copies) + temps = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + + foreach_list_safe(node, instructions) { + ir_instruction *inst = (ir_instruction *) node; + + if (inst->as_function()) + continue; + + ir_variable *var = inst->as_variable(); + if ((var != NULL) && (var->mode != ir_var_temporary)) + continue; + + assert(inst->as_assignment() + || ((var != NULL) && (var->mode == ir_var_temporary))); + + if (make_copies) { + inst = inst->clone(target, NULL); + + if (var != NULL) + hash_table_insert(temps, inst, var); + else + remap_variables(inst, target, temps); + } else { + inst->remove(); + } + + last->insert_after(inst); + last = inst; + } + + if (make_copies) + hash_table_dtor(temps); + + return last; +} + +/** + * Get the function signature for main from a shader + */ +static ir_function_signature * +get_main_function_signature(gl_shader *sh) +{ + ir_function *const f = sh->symbols->get_function("main"); + if (f != NULL) { + exec_list void_parameters; + + /* Look for the 'void main()' signature and ensure that it's defined. + * This keeps the linker from accidentally pick a shader that just + * contains a prototype for main. + * + * We don't have to check for multiple definitions of main (in multiple + * shaders) because that would have already been caught above. + */ + ir_function_signature *sig = f->matching_signature(&void_parameters); + if ((sig != NULL) && sig->is_defined) { + return sig; + } + } + + return NULL; +} + + +/** + * Combine a group of shaders for a single stage to generate a linked shader + * + * \note + * If this function is supplied a single shader, it is cloned, and the new + * shader is returned. + */ +static struct gl_shader * +link_intrastage_shaders(void *mem_ctx, + struct gl_context *ctx, + struct gl_shader_program *prog, + struct gl_shader **shader_list, + unsigned num_shaders) +{ + /* Check that global variables defined in multiple shaders are consistent. + */ + if (!cross_validate_globals(prog, shader_list, num_shaders, false)) + return NULL; + + /* Check that there is only a single definition of each function signature + * across all shaders. + */ + for (unsigned i = 0; i < (num_shaders - 1); i++) { + foreach_list(node, shader_list[i]->ir) { + ir_function *const f = ((ir_instruction *) node)->as_function(); + + if (f == NULL) + continue; + + for (unsigned j = i + 1; j < num_shaders; j++) { + ir_function *const other = + shader_list[j]->symbols->get_function(f->name); + + /* If the other shader has no function (and therefore no function + * signatures) with the same name, skip to the next shader. + */ + if (other == NULL) + continue; + + foreach_iter (exec_list_iterator, iter, *f) { + ir_function_signature *sig = + (ir_function_signature *) iter.get(); + + if (!sig->is_defined || sig->is_builtin) + continue; + + ir_function_signature *other_sig = + other->exact_matching_signature(& sig->parameters); + + if ((other_sig != NULL) && other_sig->is_defined + && !other_sig->is_builtin) { + linker_error(prog, "function `%s' is multiply defined", + f->name); + return NULL; + } + } + } + } + } + + /* Find the shader that defines main, and make a clone of it. + * + * Starting with the clone, search for undefined references. If one is + * found, find the shader that defines it. Clone the reference and add + * it to the shader. Repeat until there are no undefined references or + * until a reference cannot be resolved. + */ + gl_shader *main = NULL; + for (unsigned i = 0; i < num_shaders; i++) { + if (get_main_function_signature(shader_list[i]) != NULL) { + main = shader_list[i]; + break; + } + } + + if (main == NULL) { + linker_error(prog, "%s shader lacks `main'\n", + (shader_list[0]->Type == GL_VERTEX_SHADER) + ? "vertex" : "fragment"); + return NULL; + } + + gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type); + linked->ir = new(linked) exec_list; + clone_ir_list(mem_ctx, linked->ir, main->ir); + + populate_symbol_table(linked); + + /* The a pointer to the main function in the final linked shader (i.e., the + * copy of the original shader that contained the main function). + */ + ir_function_signature *const main_sig = get_main_function_signature(linked); + + /* Move any instructions other than variable declarations or function + * declarations into main. + */ + exec_node *insertion_point = + move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false, + linked); + + for (unsigned i = 0; i < num_shaders; i++) { + if (shader_list[i] == main) + continue; + + insertion_point = move_non_declarations(shader_list[i]->ir, + insertion_point, true, linked); + } + + /* Resolve initializers for global variables in the linked shader. + */ + unsigned num_linking_shaders = num_shaders; + for (unsigned i = 0; i < num_shaders; i++) + num_linking_shaders += shader_list[i]->num_builtins_to_link; + + gl_shader **linking_shaders = + (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *)); + + memcpy(linking_shaders, shader_list, + sizeof(linking_shaders[0]) * num_shaders); + + unsigned idx = num_shaders; + for (unsigned i = 0; i < num_shaders; i++) { + memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link, + sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link); + idx += shader_list[i]->num_builtins_to_link; + } + + assert(idx == num_linking_shaders); + + if (!link_function_calls(prog, linked, linking_shaders, + num_linking_shaders)) { + ctx->Driver.DeleteShader(ctx, linked); + linked = NULL; + } + + free(linking_shaders); + +#ifdef DEBUG + /* At this point linked should contain all of the linked IR, so + * validate it to make sure nothing went wrong. + */ + if (linked) + validate_ir_tree(linked->ir); +#endif + + /* Make a pass over all variable declarations to ensure that arrays with + * unspecified sizes have a size specified. The size is inferred from the + * max_array_access field. + */ + if (linked != NULL) { + class array_sizing_visitor : public ir_hierarchical_visitor { + public: + virtual ir_visitor_status visit(ir_variable *var) + { + if (var->type->is_array() && (var->type->length == 0)) { + const glsl_type *type = + glsl_type::get_array_instance(var->type->fields.array, + var->max_array_access + 1); + + assert(type != NULL); + var->type = type; + } + + return visit_continue; + } + } v; + + v.run(linked->ir); + } + + return linked; +} + + +struct uniform_node { + exec_node link; + struct gl_uniform *u; + unsigned slots; +}; + +/** + * Update the sizes of linked shader uniform arrays to the maximum + * array index used. + * + * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec: + * + * If one or more elements of an array are active, + * GetActiveUniform will return the name of the array in name, + * subject to the restrictions listed above. The type of the array + * is returned in type. The size parameter contains the highest + * array element index used, plus one. The compiler or linker + * determines the highest index used. There will be only one + * active uniform reported by the GL per uniform array. + + */ +static void +update_array_sizes(struct gl_shader_program *prog) +{ + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + foreach_list(node, prog->_LinkedShaders[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_uniform && + var->mode != ir_var_in && + var->mode != ir_var_out) || + !var->type->is_array()) + continue; + + unsigned int size = var->max_array_access; + for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) { + if (prog->_LinkedShaders[j] == NULL) + continue; + + foreach_list(node2, prog->_LinkedShaders[j]->ir) { + ir_variable *other_var = ((ir_instruction *) node2)->as_variable(); + if (!other_var) + continue; + + if (strcmp(var->name, other_var->name) == 0 && + other_var->max_array_access > size) { + size = other_var->max_array_access; + } + } + } + + if (size + 1 != var->type->fields.array->length) { + /* If this is a built-in uniform (i.e., it's backed by some + * fixed-function state), adjust the number of state slots to + * match the new array size. The number of slots per array entry + * is not known. It seems safe to assume that the total number of + * slots is an integer multiple of the number of array elements. + * Determine the number of slots per array element by dividing by + * the old (total) size. + */ + if (var->num_state_slots > 0) { + var->num_state_slots = (size + 1) + * (var->num_state_slots / var->type->length); + } + + var->type = glsl_type::get_array_instance(var->type->fields.array, + size + 1); + /* FINISHME: We should update the types of array + * dereferences of this variable now. + */ + } + } + } +} + +static void +add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht, + const char *name, const glsl_type *type, GLenum shader_type, + unsigned *next_shader_pos, unsigned *total_uniforms) +{ + if (type->is_record()) { + for (unsigned int i = 0; i < type->length; i++) { + const glsl_type *field_type = type->fields.structure[i].type; + char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name, + type->fields.structure[i].name); + + add_uniform(mem_ctx, uniforms, ht, field_name, field_type, + shader_type, next_shader_pos, total_uniforms); + } + } else { + uniform_node *n = (uniform_node *) hash_table_find(ht, name); + unsigned int vec4_slots; + const glsl_type *array_elem_type = NULL; + + if (type->is_array()) { + array_elem_type = type->fields.array; + /* Array of structures. */ + if (array_elem_type->is_record()) { + for (unsigned int i = 0; i < type->length; i++) { + char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i); + add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type, + shader_type, next_shader_pos, total_uniforms); + } + return; + } + } + + /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out + * vectors to vec4 slots. + */ + if (type->is_array()) { + if (array_elem_type->is_sampler()) + vec4_slots = type->length; + else + vec4_slots = type->length * array_elem_type->matrix_columns; + } else if (type->is_sampler()) { + vec4_slots = 1; + } else { + vec4_slots = type->matrix_columns; + } + + if (n == NULL) { + n = (uniform_node *) calloc(1, sizeof(struct uniform_node)); + n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform)); + n->slots = vec4_slots; + + n->u->Name = strdup(name); + n->u->Type = type; + n->u->VertPos = -1; + n->u->FragPos = -1; + n->u->GeomPos = -1; + (*total_uniforms)++; + + hash_table_insert(ht, n, name); + uniforms->push_tail(& n->link); + } + + switch (shader_type) { + case GL_VERTEX_SHADER: + n->u->VertPos = *next_shader_pos; + break; + case GL_FRAGMENT_SHADER: + n->u->FragPos = *next_shader_pos; + break; + case GL_GEOMETRY_SHADER: + n->u->GeomPos = *next_shader_pos; + break; + } + + (*next_shader_pos) += vec4_slots; + } +} + +void +assign_uniform_locations(struct gl_shader_program *prog) +{ + /* */ + exec_list uniforms; + unsigned total_uniforms = 0; + hash_table *ht = hash_table_ctor(32, hash_table_string_hash, + hash_table_string_compare); + void *mem_ctx = ralloc_context(NULL); + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + unsigned next_position = 0; + + foreach_list(node, prog->_LinkedShaders[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_uniform)) + continue; + + if (strncmp(var->name, "gl_", 3) == 0) { + /* At the moment, we don't allocate uniform locations for + * builtin uniforms. It's permitted by spec, and we'll + * likely switch to doing that at some point, but not yet. + */ + continue; + } + + var->location = next_position; + add_uniform(mem_ctx, &uniforms, ht, var->name, var->type, + prog->_LinkedShaders[i]->Type, + &next_position, &total_uniforms); + } + } + + ralloc_free(mem_ctx); + + gl_uniform_list *ul = (gl_uniform_list *) + calloc(1, sizeof(gl_uniform_list)); + + ul->Size = total_uniforms; + ul->NumUniforms = total_uniforms; + ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform)); + + unsigned idx = 0; + uniform_node *next; + for (uniform_node *node = (uniform_node *) uniforms.head + ; node->link.next != NULL + ; node = next) { + next = (uniform_node *) node->link.next; + + node->link.remove(); + memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform)); + idx++; + + free(node->u); + free(node); + } + + hash_table_dtor(ht); + + prog->Uniforms = ul; +} + + +/** + * Find a contiguous set of available bits in a bitmask. + * + * \param used_mask Bits representing used (1) and unused (0) locations + * \param needed_count Number of contiguous bits needed. + * + * \return + * Base location of the available bits on success or -1 on failure. + */ +int +find_available_slots(unsigned used_mask, unsigned needed_count) +{ + unsigned needed_mask = (1 << needed_count) - 1; + const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count; + + /* The comparison to 32 is redundant, but without it GCC emits "warning: + * cannot optimize possibly infinite loops" for the loop below. + */ + if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32)) + return -1; + + for (int i = 0; i <= max_bit_to_test; i++) { + if ((needed_mask & ~used_mask) == needed_mask) + return i; + + needed_mask <<= 1; + } + + return -1; +} + + +/** + * Assign locations for either VS inputs for FS outputs + * + * \param prog Shader program whose variables need locations assigned + * \param target_index Selector for the program target to receive location + * assignmnets. Must be either \c MESA_SHADER_VERTEX or + * \c MESA_SHADER_FRAGMENT. + * \param max_index Maximum number of generic locations. This corresponds + * to either the maximum number of draw buffers or the + * maximum number of generic attributes. + * + * \return + * If locations are successfully assigned, true is returned. Otherwise an + * error is emitted to the shader link log and false is returned. + * + * \bug + * Locations set via \c glBindFragDataLocation are not currently supported. + * Only locations assigned automatically by the linker, explicitly set by a + * layout qualifier, or explicitly set by a built-in variable (e.g., \c + * gl_FragColor) are supported for fragment shaders. + */ +bool +assign_attribute_or_color_locations(gl_shader_program *prog, + unsigned target_index, + unsigned max_index) +{ + /* Mark invalid locations as being used. + */ + unsigned used_locations = (max_index >= 32) + ? ~0 : ~((1 << max_index) - 1); + + assert((target_index == MESA_SHADER_VERTEX) + || (target_index == MESA_SHADER_FRAGMENT)); + + gl_shader *const sh = prog->_LinkedShaders[target_index]; + if (sh == NULL) + return true; + + /* Operate in a total of four passes. + * + * 1. Invalidate the location assignments for all vertex shader inputs. + * + * 2. Assign locations for inputs that have user-defined (via + * glBindVertexAttribLocation) locations. + * + * 3. Sort the attributes without assigned locations by number of slots + * required in decreasing order. Fragmentation caused by attribute + * locations assigned by the application may prevent large attributes + * from having enough contiguous space. + * + * 4. Assign locations to any inputs without assigned locations. + */ + + const int generic_base = (target_index == MESA_SHADER_VERTEX) + ? (int) VERT_ATTRIB_GENERIC0 : (int) FRAG_RESULT_DATA0; + + const enum ir_variable_mode direction = + (target_index == MESA_SHADER_VERTEX) ? ir_var_in : ir_var_out; + + + invalidate_variable_locations(sh, direction, generic_base); + + if ((target_index == MESA_SHADER_VERTEX) && (prog->Attributes != NULL)) { + for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) { + ir_variable *const var = + sh->symbols->get_variable(prog->Attributes->Parameters[i].Name); + + /* Note: attributes that occupy multiple slots, such as arrays or + * matrices, may appear in the attrib array multiple times. + */ + if ((var == NULL) || (var->location != -1)) + continue; + + /* From page 61 of the OpenGL 4.0 spec: + * + * "LinkProgram will fail if the attribute bindings assigned by + * BindAttribLocation do not leave not enough space to assign a + * location for an active matrix attribute or an active attribute + * array, both of which require multiple contiguous generic + * attributes." + * + * Previous versions of the spec contain similar language but omit the + * bit about attribute arrays. + * + * Page 61 of the OpenGL 4.0 spec also says: + * + * "It is possible for an application to bind more than one + * attribute name to the same location. This is referred to as + * aliasing. This will only work if only one of the aliased + * attributes is active in the executable program, or if no path + * through the shader consumes more than one attribute of a set + * of attributes aliased to the same location. A link error can + * occur if the linker determines that every path through the + * shader consumes multiple aliased attributes, but + * implementations are not required to generate an error in this + * case." + * + * These two paragraphs are either somewhat contradictory, or I don't + * fully understand one or both of them. + */ + /* FINISHME: The code as currently written does not support attribute + * FINISHME: location aliasing (see comment above). + */ + const int attr = prog->Attributes->Parameters[i].StateIndexes[0]; + const unsigned slots = count_attribute_slots(var->type); + + /* Mask representing the contiguous slots that will be used by this + * attribute. + */ + const unsigned use_mask = (1 << slots) - 1; + + /* Generate a link error if the set of bits requested for this + * attribute overlaps any previously allocated bits. + */ + if ((~(use_mask << attr) & used_locations) != used_locations) { + linker_error(prog, + "insufficient contiguous attribute locations " + "available for vertex shader input `%s'", + var->name); + return false; + } + + var->location = VERT_ATTRIB_GENERIC0 + attr; + used_locations |= (use_mask << attr); + } + } + + /* Temporary storage for the set of attributes that need locations assigned. + */ + struct temp_attr { + unsigned slots; + ir_variable *var; + + /* Used below in the call to qsort. */ + static int compare(const void *a, const void *b) + { + const temp_attr *const l = (const temp_attr *) a; + const temp_attr *const r = (const temp_attr *) b; + + /* Reversed because we want a descending order sort below. */ + return r->slots - l->slots; + } + } to_assign[16]; + + unsigned num_attr = 0; + + foreach_list(node, sh->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != (unsigned) direction)) + continue; + + if (var->explicit_location) { + const unsigned slots = count_attribute_slots(var->type); + const unsigned use_mask = (1 << slots) - 1; + const int attr = var->location - generic_base; + + if ((var->location >= (int)(max_index + generic_base)) + || (var->location < 0)) { + linker_error(prog, + "invalid explicit location %d specified for `%s'\n", + (var->location < 0) ? var->location : attr, + var->name); + return false; + } else if (var->location >= generic_base) { + used_locations |= (use_mask << attr); + } + } + + /* The location was explicitly assigned, nothing to do here. + */ + if (var->location != -1) + continue; + + to_assign[num_attr].slots = count_attribute_slots(var->type); + to_assign[num_attr].var = var; + num_attr++; + } + + /* If all of the attributes were assigned locations by the application (or + * are built-in attributes with fixed locations), return early. This should + * be the common case. + */ + if (num_attr == 0) + return true; + + qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare); + + if (target_index == MESA_SHADER_VERTEX) { + /* VERT_ATTRIB_GENERIC0 is a pseudo-alias for VERT_ATTRIB_POS. It can + * only be explicitly assigned by via glBindAttribLocation. Mark it as + * reserved to prevent it from being automatically allocated below. + */ + find_deref_visitor find("gl_Vertex"); + find.run(sh->ir); + if (find.variable_found()) + used_locations |= (1 << 0); + } + + for (unsigned i = 0; i < num_attr; i++) { + /* Mask representing the contiguous slots that will be used by this + * attribute. + */ + const unsigned use_mask = (1 << to_assign[i].slots) - 1; + + int location = find_available_slots(used_locations, to_assign[i].slots); + + if (location < 0) { + const char *const string = (target_index == MESA_SHADER_VERTEX) + ? "vertex shader input" : "fragment shader output"; + + linker_error(prog, + "insufficient contiguous attribute locations " + "available for %s `%s'", + string, to_assign[i].var->name); + return false; + } + + to_assign[i].var->location = generic_base + location; + used_locations |= (use_mask << location); + } + + return true; +} + + +/** + * Demote shader inputs and outputs that are not used in other stages + */ +void +demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) +{ + foreach_list(node, sh->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != int(mode))) + continue; + + /* A shader 'in' or 'out' variable is only really an input or output if + * its value is used by other shader stages. This will cause the variable + * to have a location assigned. + */ + if (var->location == -1) { + var->mode = ir_var_auto; + } + } +} + + +bool +assign_varying_locations(struct gl_context *ctx, + struct gl_shader_program *prog, + gl_shader *producer, gl_shader *consumer) +{ + /* FINISHME: Set dynamically when geometry shader support is added. */ + unsigned output_index = VERT_RESULT_VAR0; + unsigned input_index = FRAG_ATTRIB_VAR0; + + /* Operate in a total of three passes. + * + * 1. Assign locations for any matching inputs and outputs. + * + * 2. Mark output variables in the producer that do not have locations as + * not being outputs. This lets the optimizer eliminate them. + * + * 3. Mark input variables in the consumer that do not have locations as + * not being inputs. This lets the optimizer eliminate them. + */ + + invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); + invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); + + foreach_list(node, producer->ir) { + ir_variable *const output_var = ((ir_instruction *) node)->as_variable(); + + if ((output_var == NULL) || (output_var->mode != ir_var_out) + || (output_var->location != -1)) + continue; + + ir_variable *const input_var = + consumer->symbols->get_variable(output_var->name); + + if ((input_var == NULL) || (input_var->mode != ir_var_in)) + continue; + + assert(input_var->location == -1); + + output_var->location = output_index; + input_var->location = input_index; + + /* FINISHME: Support for "varying" records in GLSL 1.50. */ + assert(!output_var->type->is_record()); + + if (output_var->type->is_array()) { + const unsigned slots = output_var->type->length + * output_var->type->fields.array->matrix_columns; + + output_index += slots; + input_index += slots; + } else { + const unsigned slots = output_var->type->matrix_columns; + + output_index += slots; + input_index += slots; + } + } + + unsigned varying_vectors = 0; + + foreach_list(node, consumer->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_in)) + continue; + + if (var->location == -1) { + if (prog->Version <= 120) { + /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: + * + * Only those varying variables used (i.e. read) in + * the fragment shader executable must be written to + * by the vertex shader executable; declaring + * superfluous varying variables in a vertex shader is + * permissible. + * + * We interpret this text as meaning that the VS must + * write the variable for the FS to read it. See + * "glsl1-varying read but not written" in piglit. + */ + + linker_error(prog, "fragment shader varying %s not written " + "by vertex shader\n.", var->name); + } + + /* An 'in' variable is only really a shader input if its + * value is written by the previous stage. + */ + var->mode = ir_var_auto; + } else { + /* The packing rules are used for vertex shader inputs are also used + * for fragment shader inputs. + */ + varying_vectors += count_attribute_slots(var->type); + } + } + + if (ctx->API == API_OPENGLES2 || prog->Version == 100) { + if (varying_vectors > ctx->Const.MaxVarying) { + linker_error(prog, "shader uses too many varying vectors " + "(%u > %u)\n", + varying_vectors, ctx->Const.MaxVarying); + return false; + } + } else { + const unsigned float_components = varying_vectors * 4; + if (float_components > ctx->Const.MaxVarying * 4) { + linker_error(prog, "shader uses too many varying components " + "(%u > %u)\n", + float_components, ctx->Const.MaxVarying * 4); + return false; + } + } + + return true; +} + + +void +link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) +{ + void *mem_ctx = ralloc_context(NULL); // temporary linker context + + prog->LinkStatus = false; + prog->Validated = false; + prog->_Used = false; + + if (prog->InfoLog != NULL) + ralloc_free(prog->InfoLog); + + prog->InfoLog = ralloc_strdup(NULL, ""); + + /* Separate the shaders into groups based on their type. + */ + struct gl_shader **vert_shader_list; + unsigned num_vert_shaders = 0; + struct gl_shader **frag_shader_list; + unsigned num_frag_shaders = 0; + + vert_shader_list = (struct gl_shader **) + calloc(2 * prog->NumShaders, sizeof(struct gl_shader *)); + frag_shader_list = &vert_shader_list[prog->NumShaders]; + + unsigned min_version = UINT_MAX; + unsigned max_version = 0; + for (unsigned i = 0; i < prog->NumShaders; i++) { + min_version = MIN2(min_version, prog->Shaders[i]->Version); + max_version = MAX2(max_version, prog->Shaders[i]->Version); + + switch (prog->Shaders[i]->Type) { + case GL_VERTEX_SHADER: + vert_shader_list[num_vert_shaders] = prog->Shaders[i]; + num_vert_shaders++; + break; + case GL_FRAGMENT_SHADER: + frag_shader_list[num_frag_shaders] = prog->Shaders[i]; + num_frag_shaders++; + break; + case GL_GEOMETRY_SHADER: + /* FINISHME: Support geometry shaders. */ + assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER); + break; + } + } + + /* Previous to GLSL version 1.30, different compilation units could mix and + * match shading language versions. With GLSL 1.30 and later, the versions + * of all shaders must match. + */ + assert(min_version >= 100); + assert(max_version <= 130); + if ((max_version >= 130 || min_version == 100) + && min_version != max_version) { + linker_error(prog, "all shaders must use same shading " + "language version\n"); + goto done; + } + + prog->Version = max_version; + + for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] != NULL) + ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]); + + prog->_LinkedShaders[i] = NULL; + } + + /* Link all shaders for a particular stage and validate the result. + */ + if (num_vert_shaders > 0) { + gl_shader *const sh = + link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list, + num_vert_shaders); + + if (sh == NULL) + goto done; + + if (!validate_vertex_shader_executable(prog, sh)) + goto done; + + _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX], + sh); + } + + if (num_frag_shaders > 0) { + gl_shader *const sh = + link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list, + num_frag_shaders); + + if (sh == NULL) + goto done; + + if (!validate_fragment_shader_executable(prog, sh)) + goto done; + + _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT], + sh); + } + + /* Here begins the inter-stage linking phase. Some initial validation is + * performed, then locations are assigned for uniforms, attributes, and + * varyings. + */ + if (cross_validate_uniforms(prog)) { + unsigned prev; + + for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { + if (prog->_LinkedShaders[prev] != NULL) + break; + } + + /* Validate the inputs of each stage with the output of the preceding + * stage. + */ + for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + if (!cross_validate_outputs_to_inputs(prog, + prog->_LinkedShaders[prev], + prog->_LinkedShaders[i])) + goto done; + + prev = i; + } + + prog->LinkStatus = true; + } + + /* Do common optimization before assigning storage for attributes, + * uniforms, and varyings. Later optimization could possibly make + * some of that unused. + */ + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + detect_recursion_linked(prog, prog->_LinkedShaders[i]->ir); + if (!prog->LinkStatus) + goto done; + + while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) + ; + } + + update_array_sizes(prog); + + assign_uniform_locations(prog); + + /* FINISHME: The value of the max_attribute_index parameter is + * FINISHME: implementation dependent based on the value of + * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be + * FINISHME: at least 16, so hardcode 16 for now. + */ + if (!assign_attribute_or_color_locations(prog, MESA_SHADER_VERTEX, 16)) { + goto done; + } + + if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, ctx->Const.MaxDrawBuffers)) { + goto done; + } + + unsigned prev; + for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { + if (prog->_LinkedShaders[prev] != NULL) + break; + } + + for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + if (!assign_varying_locations(ctx, prog, + prog->_LinkedShaders[prev], + prog->_LinkedShaders[i])) { + goto done; + } + + prev = i; + } + + if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { + demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], + ir_var_out); + } + + if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { + gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]; + + demote_shader_inputs_and_outputs(sh, ir_var_in); + demote_shader_inputs_and_outputs(sh, ir_var_inout); + demote_shader_inputs_and_outputs(sh, ir_var_out); + } + + if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) { + gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]; + + demote_shader_inputs_and_outputs(sh, ir_var_in); + } + + /* OpenGL ES requires that a vertex shader and a fragment shader both be + * present in a linked program. By checking for use of shading language + * version 1.00, we also catch the GL_ARB_ES2_compatibility case. + */ + if (ctx->API == API_OPENGLES2 || prog->Version == 100) { + if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) { + linker_error(prog, "program lacks a vertex shader\n"); + } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { + linker_error(prog, "program lacks a fragment shader\n"); + } + } + + /* FINISHME: Assign fragment shader output locations. */ + +done: + free(vert_shader_list); + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + /* Retain any live IR, but trash the rest. */ + reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir); + } + + ralloc_free(mem_ctx); +} diff --git a/mesalib/src/glsl/loop_controls.cpp b/mesalib/src/glsl/loop_controls.cpp index 9b77ef460..9acbadc50 100644 --- a/mesalib/src/glsl/loop_controls.cpp +++ b/mesalib/src/glsl/loop_controls.cpp @@ -1,304 +1,304 @@ -/* - * 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 -#include "main/compiler.h" -#include "glsl_types.h" -#include "loop_analysis.h" -#include "ir_hierarchical_visitor.h" - -/** - * Find an initializer of a variable outside a loop - * - * Works backwards from the loop to find the pre-loop value of the variable. - * This is used, for example, to find the initial value of loop induction - * variables. - * - * \param loop Loop where \c var is an induction variable - * \param var Variable whose initializer is to be found - * - * \return - * The \c ir_rvalue assigned to the variable outside the loop. May return - * \c NULL if no initializer can be found. - */ -ir_rvalue * -find_initial_value(ir_loop *loop, ir_variable *var) -{ - for (exec_node *node = loop->prev; - !node->is_head_sentinel(); - node = node->prev) { - ir_instruction *ir = (ir_instruction *) node; - - switch (ir->ir_type) { - case ir_type_call: - case ir_type_loop: - case ir_type_loop_jump: - case ir_type_return: - case ir_type_if: - return NULL; - - case ir_type_function: - case ir_type_function_signature: - assert(!"Should not get here."); - return NULL; - - case ir_type_assignment: { - ir_assignment *assign = ir->as_assignment(); - ir_variable *assignee = assign->lhs->whole_variable_referenced(); - - if (assignee == var) - return (assign->condition != NULL) ? NULL : assign->rhs; - - break; - } - - default: - break; - } - } - - return NULL; -} - - -int -calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, - enum ir_expression_operation op) -{ - if (from == NULL || to == NULL || increment == NULL) - return -1; - - void *mem_ctx = ralloc_context(NULL); - - ir_expression *const sub = - new(mem_ctx) ir_expression(ir_binop_sub, from->type, to, from); - - ir_expression *const div = - new(mem_ctx) ir_expression(ir_binop_div, sub->type, sub, increment); - - ir_constant *iter = div->constant_expression_value(); - - if (iter == NULL) - return -1; - - if (!iter->type->is_integer()) { - ir_rvalue *cast = - new(mem_ctx) ir_expression(ir_unop_f2i, glsl_type::int_type, iter, - NULL); - - iter = cast->constant_expression_value(); - } - - int iter_value = iter->get_int_component(0); - - /* Make sure that the calculated number of iterations satisfies the exit - * condition. This is needed to catch off-by-one errors and some types of - * ill-formed loops. For example, we need to detect that the following - * loop does not have a maximum iteration count. - * - * for (float x = 0.0; x != 0.9; x += 0.2) - * ; - */ - const int bias[] = { -1, 0, 1 }; - bool valid_loop = false; - - for (unsigned i = 0; i < Elements(bias); i++) { - iter = (increment->type->is_integer()) - ? new(mem_ctx) ir_constant(iter_value + bias[i]) - : new(mem_ctx) ir_constant(float(iter_value + bias[i])); - - ir_expression *const mul = - new(mem_ctx) ir_expression(ir_binop_mul, increment->type, iter, - increment); - - ir_expression *const add = - new(mem_ctx) ir_expression(ir_binop_add, mul->type, mul, from); - - ir_expression *const cmp = - new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to); - - ir_constant *const cmp_result = cmp->constant_expression_value(); - - assert(cmp_result != NULL); - if (cmp_result->get_bool_component(0)) { - iter_value += bias[i]; - valid_loop = true; - break; - } - } - - ralloc_free(mem_ctx); - return (valid_loop) ? iter_value : -1; -} - - -class loop_control_visitor : public ir_hierarchical_visitor { -public: - loop_control_visitor(loop_state *state) - { - this->state = state; - this->progress = false; - } - - virtual ir_visitor_status visit_leave(ir_loop *ir); - - loop_state *state; - - bool progress; -}; - - -ir_visitor_status -loop_control_visitor::visit_leave(ir_loop *ir) -{ - loop_variable_state *const ls = this->state->get(ir); - - /* If we've entered a loop that hasn't been analyzed, something really, - * really bad has happened. - */ - if (ls == NULL) { - assert(ls != NULL); - return visit_continue; - } - - /* Search the loop terminating conditions for one of the form 'i < c' where - * i is a loop induction variable, c is a constant, and < is any relative - * operator. - */ - int max_iterations = ls->max_iterations; - - if(ir->from && ir->to && ir->increment) - max_iterations = calculate_iterations(ir->from, ir->to, ir->increment, (ir_expression_operation)ir->cmp); - - if(max_iterations < 0) - max_iterations = INT_MAX; - - foreach_list(node, &ls->terminators) { - loop_terminator *t = (loop_terminator *) node; - ir_if *if_stmt = t->ir; - - /* If-statements can be either 'if (expr)' or 'if (deref)'. We only care - * about the former here. - */ - ir_expression *cond = if_stmt->condition->as_expression(); - if (cond == NULL) - continue; - - switch (cond->operation) { - case ir_binop_less: - case ir_binop_greater: - case ir_binop_lequal: - case ir_binop_gequal: { - /* The expressions that we care about will either be of the form - * 'counter < limit' or 'limit < counter'. Figure out which is - * which. - */ - ir_rvalue *counter = cond->operands[0]->as_dereference_variable(); - ir_constant *limit = cond->operands[1]->as_constant(); - enum ir_expression_operation cmp = cond->operation; - - if (limit == NULL) { - counter = cond->operands[1]->as_dereference_variable(); - limit = cond->operands[0]->as_constant(); - - switch (cmp) { - case ir_binop_less: cmp = ir_binop_gequal; break; - case ir_binop_greater: cmp = ir_binop_lequal; break; - case ir_binop_lequal: cmp = ir_binop_greater; break; - case ir_binop_gequal: cmp = ir_binop_less; break; - default: assert(!"Should not get here."); - } - } - - if ((counter == NULL) || (limit == NULL)) - break; - - ir_variable *var = counter->variable_referenced(); - - ir_rvalue *init = find_initial_value(ir, var); - - foreach_list(iv_node, &ls->induction_variables) { - loop_variable *lv = (loop_variable *) iv_node; - - if (lv->var == var) { - const int iterations = calculate_iterations(init, limit, - lv->increment, - cmp); - if (iterations >= 0) { - /* If the new iteration count is lower than the previously - * believed iteration count, update the loop control values. - */ - if (iterations < max_iterations) { - ir->from = init->clone(ir, NULL); - ir->to = limit->clone(ir, NULL); - ir->increment = lv->increment->clone(ir, NULL); - ir->counter = lv->var; - ir->cmp = cmp; - - max_iterations = iterations; - } - - /* Remove the conditional break statement. The loop - * controls are now set such that the exit condition will be - * satisfied. - */ - if_stmt->remove(); - - assert(ls->num_loop_jumps > 0); - ls->num_loop_jumps--; - - this->progress = true; - } - - break; - } - } - break; - } - - default: - break; - } - } - - /* If we have proven the one of the loop exit conditions is satisifed before - * running the loop once, remove the loop. - */ - if (max_iterations == 0) - ir->remove(); - else - ls->max_iterations = max_iterations; - - return visit_continue; -} - - -bool -set_loop_controls(exec_list *instructions, loop_state *ls) -{ - loop_control_visitor v(ls); - - v.run(instructions); - - return v.progress; -} +/* + * 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 +#include "main/compiler.h" +#include "glsl_types.h" +#include "loop_analysis.h" +#include "ir_hierarchical_visitor.h" + +/** + * Find an initializer of a variable outside a loop + * + * Works backwards from the loop to find the pre-loop value of the variable. + * This is used, for example, to find the initial value of loop induction + * variables. + * + * \param loop Loop where \c var is an induction variable + * \param var Variable whose initializer is to be found + * + * \return + * The \c ir_rvalue assigned to the variable outside the loop. May return + * \c NULL if no initializer can be found. + */ +ir_rvalue * +find_initial_value(ir_loop *loop, ir_variable *var) +{ + for (exec_node *node = loop->prev; + !node->is_head_sentinel(); + node = node->prev) { + ir_instruction *ir = (ir_instruction *) node; + + switch (ir->ir_type) { + case ir_type_call: + case ir_type_loop: + case ir_type_loop_jump: + case ir_type_return: + case ir_type_if: + return NULL; + + case ir_type_function: + case ir_type_function_signature: + assert(!"Should not get here."); + return NULL; + + case ir_type_assignment: { + ir_assignment *assign = ir->as_assignment(); + ir_variable *assignee = assign->lhs->whole_variable_referenced(); + + if (assignee == var) + return (assign->condition != NULL) ? NULL : assign->rhs; + + break; + } + + default: + break; + } + } + + return NULL; +} + + +int +calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, + enum ir_expression_operation op) +{ + if (from == NULL || to == NULL || increment == NULL) + return -1; + + void *mem_ctx = ralloc_context(NULL); + + ir_expression *const sub = + new(mem_ctx) ir_expression(ir_binop_sub, from->type, to, from); + + ir_expression *const div = + new(mem_ctx) ir_expression(ir_binop_div, sub->type, sub, increment); + + ir_constant *iter = div->constant_expression_value(); + + if (iter == NULL) + return -1; + + if (!iter->type->is_integer()) { + ir_rvalue *cast = + new(mem_ctx) ir_expression(ir_unop_f2i, glsl_type::int_type, iter, + NULL); + + iter = cast->constant_expression_value(); + } + + int iter_value = iter->get_int_component(0); + + /* Make sure that the calculated number of iterations satisfies the exit + * condition. This is needed to catch off-by-one errors and some types of + * ill-formed loops. For example, we need to detect that the following + * loop does not have a maximum iteration count. + * + * for (float x = 0.0; x != 0.9; x += 0.2) + * ; + */ + const int bias[] = { -1, 0, 1 }; + bool valid_loop = false; + + for (unsigned i = 0; i < Elements(bias); i++) { + iter = (increment->type->is_integer()) + ? new(mem_ctx) ir_constant(iter_value + bias[i]) + : new(mem_ctx) ir_constant(float(iter_value + bias[i])); + + ir_expression *const mul = + new(mem_ctx) ir_expression(ir_binop_mul, increment->type, iter, + increment); + + ir_expression *const add = + new(mem_ctx) ir_expression(ir_binop_add, mul->type, mul, from); + + ir_expression *const cmp = + new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to); + + ir_constant *const cmp_result = cmp->constant_expression_value(); + + assert(cmp_result != NULL); + if (cmp_result->get_bool_component(0)) { + iter_value += bias[i]; + valid_loop = true; + break; + } + } + + ralloc_free(mem_ctx); + return (valid_loop) ? iter_value : -1; +} + + +class loop_control_visitor : public ir_hierarchical_visitor { +public: + loop_control_visitor(loop_state *state) + { + this->state = state; + this->progress = false; + } + + virtual ir_visitor_status visit_leave(ir_loop *ir); + + loop_state *state; + + bool progress; +}; + + +ir_visitor_status +loop_control_visitor::visit_leave(ir_loop *ir) +{ + loop_variable_state *const ls = this->state->get(ir); + + /* If we've entered a loop that hasn't been analyzed, something really, + * really bad has happened. + */ + if (ls == NULL) { + assert(ls != NULL); + return visit_continue; + } + + /* Search the loop terminating conditions for one of the form 'i < c' where + * i is a loop induction variable, c is a constant, and < is any relative + * operator. + */ + int max_iterations = ls->max_iterations; + + if(ir->from && ir->to && ir->increment) + max_iterations = calculate_iterations(ir->from, ir->to, ir->increment, (ir_expression_operation)ir->cmp); + + if(max_iterations < 0) + max_iterations = INT_MAX; + + foreach_list(node, &ls->terminators) { + loop_terminator *t = (loop_terminator *) node; + ir_if *if_stmt = t->ir; + + /* If-statements can be either 'if (expr)' or 'if (deref)'. We only care + * about the former here. + */ + ir_expression *cond = if_stmt->condition->as_expression(); + if (cond == NULL) + continue; + + switch (cond->operation) { + case ir_binop_less: + case ir_binop_greater: + case ir_binop_lequal: + case ir_binop_gequal: { + /* The expressions that we care about will either be of the form + * 'counter < limit' or 'limit < counter'. Figure out which is + * which. + */ + ir_rvalue *counter = cond->operands[0]->as_dereference_variable(); + ir_constant *limit = cond->operands[1]->as_constant(); + enum ir_expression_operation cmp = cond->operation; + + if (limit == NULL) { + counter = cond->operands[1]->as_dereference_variable(); + limit = cond->operands[0]->as_constant(); + + switch (cmp) { + case ir_binop_less: cmp = ir_binop_gequal; break; + case ir_binop_greater: cmp = ir_binop_lequal; break; + case ir_binop_lequal: cmp = ir_binop_greater; break; + case ir_binop_gequal: cmp = ir_binop_less; break; + default: assert(!"Should not get here."); + } + } + + if ((counter == NULL) || (limit == NULL)) + break; + + ir_variable *var = counter->variable_referenced(); + + ir_rvalue *init = find_initial_value(ir, var); + + foreach_list(iv_node, &ls->induction_variables) { + loop_variable *lv = (loop_variable *) iv_node; + + if (lv->var == var) { + const int iterations = calculate_iterations(init, limit, + lv->increment, + cmp); + if (iterations >= 0) { + /* If the new iteration count is lower than the previously + * believed iteration count, update the loop control values. + */ + if (iterations < max_iterations) { + ir->from = init->clone(ir, NULL); + ir->to = limit->clone(ir, NULL); + ir->increment = lv->increment->clone(ir, NULL); + ir->counter = lv->var; + ir->cmp = cmp; + + max_iterations = iterations; + } + + /* Remove the conditional break statement. The loop + * controls are now set such that the exit condition will be + * satisfied. + */ + if_stmt->remove(); + + assert(ls->num_loop_jumps > 0); + ls->num_loop_jumps--; + + this->progress = true; + } + + break; + } + } + break; + } + + default: + break; + } + } + + /* If we have proven the one of the loop exit conditions is satisifed before + * running the loop once, remove the loop. + */ + if (max_iterations == 0) + ir->remove(); + else + ls->max_iterations = max_iterations; + + return visit_continue; +} + + +bool +set_loop_controls(exec_list *instructions, loop_state *ls) +{ + loop_control_visitor v(ls); + + v.run(instructions); + + return v.progress; +} diff --git a/mesalib/src/glsl/lower_mat_op_to_vec.cpp b/mesalib/src/glsl/lower_mat_op_to_vec.cpp index d464e8bf3..a371afc14 100644 --- a/mesalib/src/glsl/lower_mat_op_to_vec.cpp +++ b/mesalib/src/glsl/lower_mat_op_to_vec.cpp @@ -1,428 +1,428 @@ -/* - * 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. - */ - -/** - * \file lower_mat_op_to_vec.cpp - * - * Breaks matrix operation expressions down to a series of vector operations. - * - * Generally this is how we have to codegen matrix operations for a - * GPU, so this gives us the chance to constant fold operations on a - * column or row. - */ - -#include "ir.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" - -class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor { -public: - ir_mat_op_to_vec_visitor() - { - this->made_progress = false; - this->mem_ctx = NULL; - } - - ir_visitor_status visit_leave(ir_assignment *); - - ir_dereference *get_column(ir_dereference *val, int col); - ir_rvalue *get_element(ir_dereference *val, int col, int row); - - void do_mul_mat_mat(ir_dereference *result, - ir_dereference *a, ir_dereference *b); - void do_mul_mat_vec(ir_dereference *result, - ir_dereference *a, ir_dereference *b); - void do_mul_vec_mat(ir_dereference *result, - ir_dereference *a, ir_dereference *b); - void do_mul_mat_scalar(ir_dereference *result, - ir_dereference *a, ir_dereference *b); - void do_equal_mat_mat(ir_dereference *result, ir_dereference *a, - ir_dereference *b, bool test_equal); - - void *mem_ctx; - bool made_progress; -}; - -static bool -mat_op_to_vec_predicate(ir_instruction *ir) -{ - ir_expression *expr = ir->as_expression(); - unsigned int i; - - if (!expr) - return false; - - for (i = 0; i < expr->get_num_operands(); i++) { - if (expr->operands[i]->type->is_matrix()) - return true; - } - - return false; -} - -bool -do_mat_op_to_vec(exec_list *instructions) -{ - ir_mat_op_to_vec_visitor v; - - /* Pull out any matrix expression to a separate assignment to a - * temp. This will make our handling of the breakdown to - * operations on the matrix's vector components much easier. - */ - do_expression_flattening(instructions, mat_op_to_vec_predicate); - - visit_list_elements(&v, instructions); - - return v.made_progress; -} - -ir_rvalue * -ir_mat_op_to_vec_visitor::get_element(ir_dereference *val, int col, int row) -{ - val = get_column(val, col); - - return new(mem_ctx) ir_swizzle(val, row, 0, 0, 0, 1); -} - -ir_dereference * -ir_mat_op_to_vec_visitor::get_column(ir_dereference *val, int row) -{ - val = val->clone(mem_ctx, NULL); - - if (val->type->is_matrix()) { - val = new(mem_ctx) ir_dereference_array(val, - new(mem_ctx) ir_constant(row)); - } - - return val; -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result, - ir_dereference *a, - ir_dereference *b) -{ - int b_col, i; - ir_assignment *assign; - ir_expression *expr; - - for (b_col = 0; b_col < b->type->matrix_columns; b_col++) { - /* first column */ - expr = new(mem_ctx) ir_expression(ir_binop_mul, - get_column(a, 0), - get_element(b, b_col, 0)); - - /* following columns */ - for (i = 1; i < a->type->matrix_columns; i++) { - ir_expression *mul_expr; - - mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, - get_column(a, i), - get_element(b, b_col, i)); - expr = new(mem_ctx) ir_expression(ir_binop_add, - expr, - mul_expr); - } - - assign = new(mem_ctx) ir_assignment(get_column(result, b_col), expr); - base_ir->insert_before(assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result, - ir_dereference *a, - ir_dereference *b) -{ - int i; - ir_assignment *assign; - ir_expression *expr; - - /* first column */ - expr = new(mem_ctx) ir_expression(ir_binop_mul, - get_column(a, 0), - get_element(b, 0, 0)); - - /* following columns */ - for (i = 1; i < a->type->matrix_columns; i++) { - ir_expression *mul_expr; - - mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, - get_column(a, i), - get_element(b, 0, i)); - expr = new(mem_ctx) ir_expression(ir_binop_add, expr, mul_expr); - } - - result = result->clone(mem_ctx, NULL); - assign = new(mem_ctx) ir_assignment(result, expr); - base_ir->insert_before(assign); -} - -void -ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result, - ir_dereference *a, - ir_dereference *b) -{ - int i; - - for (i = 0; i < b->type->matrix_columns; i++) { - ir_rvalue *column_result; - ir_expression *column_expr; - ir_assignment *column_assign; - - column_result = result->clone(mem_ctx, NULL); - column_result = new(mem_ctx) ir_swizzle(column_result, i, 0, 0, 0, 1); - - column_expr = new(mem_ctx) ir_expression(ir_binop_dot, - a->clone(mem_ctx, NULL), - get_column(b, i)); - - column_assign = new(mem_ctx) ir_assignment(column_result, - column_expr); - base_ir->insert_before(column_assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result, - ir_dereference *a, - ir_dereference *b) -{ - int i; - - for (i = 0; i < a->type->matrix_columns; i++) { - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(ir_binop_mul, - get_column(a, i), - b->clone(mem_ctx, NULL)); - - column_assign = new(mem_ctx) ir_assignment(get_column(result, i), - column_expr); - base_ir->insert_before(column_assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference *result, - ir_dereference *a, - ir_dereference *b, - bool test_equal) -{ - /* This essentially implements the following GLSL: - * - * bool equal(mat4 a, mat4 b) - * { - * return !any(bvec4(a[0] != b[0], - * a[1] != b[1], - * a[2] != b[2], - * a[3] != b[3]); - * } - * - * bool nequal(mat4 a, mat4 b) - * { - * return any(bvec4(a[0] != b[0], - * a[1] != b[1], - * a[2] != b[2], - * a[3] != b[3]); - * } - */ - const unsigned columns = a->type->matrix_columns; - const glsl_type *const bvec_type = - glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1); - - ir_variable *const tmp_bvec = - new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec", - ir_var_temporary); - this->base_ir->insert_before(tmp_bvec); - - for (unsigned i = 0; i < columns; i++) { - ir_expression *const cmp = - new(this->mem_ctx) ir_expression(ir_binop_any_nequal, - get_column(a, i), - get_column(b, i)); - - ir_dereference *const lhs = - new(this->mem_ctx) ir_dereference_variable(tmp_bvec); - - ir_assignment *const assign = - new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i)); - - this->base_ir->insert_before(assign); - } - - ir_rvalue *const val = new(this->mem_ctx) ir_dereference_variable(tmp_bvec); - ir_expression *any = new(this->mem_ctx) ir_expression(ir_unop_any, val); - - if (test_equal) - any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, any); - - ir_assignment *const assign = - new(mem_ctx) ir_assignment(result->clone(mem_ctx, NULL), any); - base_ir->insert_before(assign); -} - -static bool -has_matrix_operand(const ir_expression *expr, unsigned &columns) -{ - for (unsigned i = 0; i < expr->get_num_operands(); i++) { - if (expr->operands[i]->type->is_matrix()) { - columns = expr->operands[i]->type->matrix_columns; - return true; - } - } - - return false; -} - - -ir_visitor_status -ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign) -{ - ir_expression *orig_expr = orig_assign->rhs->as_expression(); - unsigned int i, matrix_columns = 1; - ir_dereference *op[2]; - - if (!orig_expr) - return visit_continue; - - if (!has_matrix_operand(orig_expr, matrix_columns)) - return visit_continue; - - assert(orig_expr->get_num_operands() <= 2); - - mem_ctx = ralloc_parent(orig_assign); - - ir_dereference_variable *result = - orig_assign->lhs->as_dereference_variable(); - assert(result); - - /* Store the expression operands in temps so we can use them - * multiple times. - */ - for (i = 0; i < orig_expr->get_num_operands(); i++) { - ir_assignment *assign; - ir_dereference *deref = orig_expr->operands[i]->as_dereference(); - - /* Avoid making a temporary if we don't need to to avoid aliasing. */ - if (deref && - deref->variable_referenced() != result->variable_referenced()) { - op[i] = deref; - continue; - } - - /* Otherwise, store the operand in a temporary generally if it's - * not a dereference. - */ - ir_variable *var = new(mem_ctx) ir_variable(orig_expr->operands[i]->type, - "mat_op_to_vec", - ir_var_temporary); - base_ir->insert_before(var); - - /* Note that we use this dereference for the assignment. That means - * that others that want to use op[i] have to clone the deref. - */ - op[i] = new(mem_ctx) ir_dereference_variable(var); - assign = new(mem_ctx) ir_assignment(op[i], orig_expr->operands[i]); - base_ir->insert_before(assign); - } - - /* OK, time to break down this matrix operation. */ - switch (orig_expr->operation) { - case ir_unop_neg: { - /* Apply the operation to each column.*/ - for (i = 0; i < matrix_columns; i++) { - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(orig_expr->operation, - get_column(op[0], i)); - - column_assign = new(mem_ctx) ir_assignment(get_column(result, i), - column_expr); - assert(column_assign->write_mask != 0); - base_ir->insert_before(column_assign); - } - break; - } - case ir_binop_add: - case ir_binop_sub: - case ir_binop_div: - case ir_binop_mod: { - /* For most operations, the matrix version is just going - * column-wise through and applying the operation to each column - * if available. - */ - for (i = 0; i < matrix_columns; i++) { - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(orig_expr->operation, - get_column(op[0], i), - get_column(op[1], i)); - - column_assign = new(mem_ctx) ir_assignment(get_column(result, i), - column_expr); - assert(column_assign->write_mask != 0); - base_ir->insert_before(column_assign); - } - break; - } - case ir_binop_mul: - if (op[0]->type->is_matrix()) { - if (op[1]->type->is_matrix()) { - do_mul_mat_mat(result, op[0], op[1]); - } else if (op[1]->type->is_vector()) { - do_mul_mat_vec(result, op[0], op[1]); - } else { - assert(op[1]->type->is_scalar()); - do_mul_mat_scalar(result, op[0], op[1]); - } - } else { - assert(op[1]->type->is_matrix()); - if (op[0]->type->is_vector()) { - do_mul_vec_mat(result, op[0], op[1]); - } else { - assert(op[0]->type->is_scalar()); - do_mul_mat_scalar(result, op[1], op[0]); - } - } - break; - - case ir_binop_all_equal: - case ir_binop_any_nequal: - do_equal_mat_mat(result, op[1], op[0], - (orig_expr->operation == ir_binop_all_equal)); - break; - - default: - printf("FINISHME: Handle matrix operation for %s\n", - orig_expr->operator_string()); - abort(); - } - orig_assign->remove(); - this->made_progress = true; - - return visit_continue; -} +/* + * 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. + */ + +/** + * \file lower_mat_op_to_vec.cpp + * + * Breaks matrix operation expressions down to a series of vector operations. + * + * Generally this is how we have to codegen matrix operations for a + * GPU, so this gives us the chance to constant fold operations on a + * column or row. + */ + +#include "ir.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" + +class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor { +public: + ir_mat_op_to_vec_visitor() + { + this->made_progress = false; + this->mem_ctx = NULL; + } + + ir_visitor_status visit_leave(ir_assignment *); + + ir_dereference *get_column(ir_dereference *val, int col); + ir_rvalue *get_element(ir_dereference *val, int col, int row); + + void do_mul_mat_mat(ir_dereference *result, + ir_dereference *a, ir_dereference *b); + void do_mul_mat_vec(ir_dereference *result, + ir_dereference *a, ir_dereference *b); + void do_mul_vec_mat(ir_dereference *result, + ir_dereference *a, ir_dereference *b); + void do_mul_mat_scalar(ir_dereference *result, + ir_dereference *a, ir_dereference *b); + void do_equal_mat_mat(ir_dereference *result, ir_dereference *a, + ir_dereference *b, bool test_equal); + + void *mem_ctx; + bool made_progress; +}; + +static bool +mat_op_to_vec_predicate(ir_instruction *ir) +{ + ir_expression *expr = ir->as_expression(); + unsigned int i; + + if (!expr) + return false; + + for (i = 0; i < expr->get_num_operands(); i++) { + if (expr->operands[i]->type->is_matrix()) + return true; + } + + return false; +} + +bool +do_mat_op_to_vec(exec_list *instructions) +{ + ir_mat_op_to_vec_visitor v; + + /* Pull out any matrix expression to a separate assignment to a + * temp. This will make our handling of the breakdown to + * operations on the matrix's vector components much easier. + */ + do_expression_flattening(instructions, mat_op_to_vec_predicate); + + visit_list_elements(&v, instructions); + + return v.made_progress; +} + +ir_rvalue * +ir_mat_op_to_vec_visitor::get_element(ir_dereference *val, int col, int row) +{ + val = get_column(val, col); + + return new(mem_ctx) ir_swizzle(val, row, 0, 0, 0, 1); +} + +ir_dereference * +ir_mat_op_to_vec_visitor::get_column(ir_dereference *val, int row) +{ + val = val->clone(mem_ctx, NULL); + + if (val->type->is_matrix()) { + val = new(mem_ctx) ir_dereference_array(val, + new(mem_ctx) ir_constant(row)); + } + + return val; +} + +void +ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result, + ir_dereference *a, + ir_dereference *b) +{ + int b_col, i; + ir_assignment *assign; + ir_expression *expr; + + for (b_col = 0; b_col < b->type->matrix_columns; b_col++) { + /* first column */ + expr = new(mem_ctx) ir_expression(ir_binop_mul, + get_column(a, 0), + get_element(b, b_col, 0)); + + /* following columns */ + for (i = 1; i < a->type->matrix_columns; i++) { + ir_expression *mul_expr; + + mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, + get_column(a, i), + get_element(b, b_col, i)); + expr = new(mem_ctx) ir_expression(ir_binop_add, + expr, + mul_expr); + } + + assign = new(mem_ctx) ir_assignment(get_column(result, b_col), expr); + base_ir->insert_before(assign); + } +} + +void +ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result, + ir_dereference *a, + ir_dereference *b) +{ + int i; + ir_assignment *assign; + ir_expression *expr; + + /* first column */ + expr = new(mem_ctx) ir_expression(ir_binop_mul, + get_column(a, 0), + get_element(b, 0, 0)); + + /* following columns */ + for (i = 1; i < a->type->matrix_columns; i++) { + ir_expression *mul_expr; + + mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, + get_column(a, i), + get_element(b, 0, i)); + expr = new(mem_ctx) ir_expression(ir_binop_add, expr, mul_expr); + } + + result = result->clone(mem_ctx, NULL); + assign = new(mem_ctx) ir_assignment(result, expr); + base_ir->insert_before(assign); +} + +void +ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result, + ir_dereference *a, + ir_dereference *b) +{ + int i; + + for (i = 0; i < b->type->matrix_columns; i++) { + ir_rvalue *column_result; + ir_expression *column_expr; + ir_assignment *column_assign; + + column_result = result->clone(mem_ctx, NULL); + column_result = new(mem_ctx) ir_swizzle(column_result, i, 0, 0, 0, 1); + + column_expr = new(mem_ctx) ir_expression(ir_binop_dot, + a->clone(mem_ctx, NULL), + get_column(b, i)); + + column_assign = new(mem_ctx) ir_assignment(column_result, + column_expr); + base_ir->insert_before(column_assign); + } +} + +void +ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result, + ir_dereference *a, + ir_dereference *b) +{ + int i; + + for (i = 0; i < a->type->matrix_columns; i++) { + ir_expression *column_expr; + ir_assignment *column_assign; + + column_expr = new(mem_ctx) ir_expression(ir_binop_mul, + get_column(a, i), + b->clone(mem_ctx, NULL)); + + column_assign = new(mem_ctx) ir_assignment(get_column(result, i), + column_expr); + base_ir->insert_before(column_assign); + } +} + +void +ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference *result, + ir_dereference *a, + ir_dereference *b, + bool test_equal) +{ + /* This essentially implements the following GLSL: + * + * bool equal(mat4 a, mat4 b) + * { + * return !any(bvec4(a[0] != b[0], + * a[1] != b[1], + * a[2] != b[2], + * a[3] != b[3]); + * } + * + * bool nequal(mat4 a, mat4 b) + * { + * return any(bvec4(a[0] != b[0], + * a[1] != b[1], + * a[2] != b[2], + * a[3] != b[3]); + * } + */ + const unsigned columns = a->type->matrix_columns; + const glsl_type *const bvec_type = + glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1); + + ir_variable *const tmp_bvec = + new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec", + ir_var_temporary); + this->base_ir->insert_before(tmp_bvec); + + for (unsigned i = 0; i < columns; i++) { + ir_expression *const cmp = + new(this->mem_ctx) ir_expression(ir_binop_any_nequal, + get_column(a, i), + get_column(b, i)); + + ir_dereference *const lhs = + new(this->mem_ctx) ir_dereference_variable(tmp_bvec); + + ir_assignment *const assign = + new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i)); + + this->base_ir->insert_before(assign); + } + + ir_rvalue *const val = new(this->mem_ctx) ir_dereference_variable(tmp_bvec); + ir_expression *any = new(this->mem_ctx) ir_expression(ir_unop_any, val); + + if (test_equal) + any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, any); + + ir_assignment *const assign = + new(mem_ctx) ir_assignment(result->clone(mem_ctx, NULL), any); + base_ir->insert_before(assign); +} + +static bool +has_matrix_operand(const ir_expression *expr, unsigned &columns) +{ + for (unsigned i = 0; i < expr->get_num_operands(); i++) { + if (expr->operands[i]->type->is_matrix()) { + columns = expr->operands[i]->type->matrix_columns; + return true; + } + } + + return false; +} + + +ir_visitor_status +ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign) +{ + ir_expression *orig_expr = orig_assign->rhs->as_expression(); + unsigned int i, matrix_columns = 1; + ir_dereference *op[2]; + + if (!orig_expr) + return visit_continue; + + if (!has_matrix_operand(orig_expr, matrix_columns)) + return visit_continue; + + assert(orig_expr->get_num_operands() <= 2); + + mem_ctx = ralloc_parent(orig_assign); + + ir_dereference_variable *result = + orig_assign->lhs->as_dereference_variable(); + assert(result); + + /* Store the expression operands in temps so we can use them + * multiple times. + */ + for (i = 0; i < orig_expr->get_num_operands(); i++) { + ir_assignment *assign; + ir_dereference *deref = orig_expr->operands[i]->as_dereference(); + + /* Avoid making a temporary if we don't need to to avoid aliasing. */ + if (deref && + deref->variable_referenced() != result->variable_referenced()) { + op[i] = deref; + continue; + } + + /* Otherwise, store the operand in a temporary generally if it's + * not a dereference. + */ + ir_variable *var = new(mem_ctx) ir_variable(orig_expr->operands[i]->type, + "mat_op_to_vec", + ir_var_temporary); + base_ir->insert_before(var); + + /* Note that we use this dereference for the assignment. That means + * that others that want to use op[i] have to clone the deref. + */ + op[i] = new(mem_ctx) ir_dereference_variable(var); + assign = new(mem_ctx) ir_assignment(op[i], orig_expr->operands[i]); + base_ir->insert_before(assign); + } + + /* OK, time to break down this matrix operation. */ + switch (orig_expr->operation) { + case ir_unop_neg: { + /* Apply the operation to each column.*/ + for (i = 0; i < matrix_columns; i++) { + ir_expression *column_expr; + ir_assignment *column_assign; + + column_expr = new(mem_ctx) ir_expression(orig_expr->operation, + get_column(op[0], i)); + + column_assign = new(mem_ctx) ir_assignment(get_column(result, i), + column_expr); + assert(column_assign->write_mask != 0); + base_ir->insert_before(column_assign); + } + break; + } + case ir_binop_add: + case ir_binop_sub: + case ir_binop_div: + case ir_binop_mod: { + /* For most operations, the matrix version is just going + * column-wise through and applying the operation to each column + * if available. + */ + for (i = 0; i < matrix_columns; i++) { + ir_expression *column_expr; + ir_assignment *column_assign; + + column_expr = new(mem_ctx) ir_expression(orig_expr->operation, + get_column(op[0], i), + get_column(op[1], i)); + + column_assign = new(mem_ctx) ir_assignment(get_column(result, i), + column_expr); + assert(column_assign->write_mask != 0); + base_ir->insert_before(column_assign); + } + break; + } + case ir_binop_mul: + if (op[0]->type->is_matrix()) { + if (op[1]->type->is_matrix()) { + do_mul_mat_mat(result, op[0], op[1]); + } else if (op[1]->type->is_vector()) { + do_mul_mat_vec(result, op[0], op[1]); + } else { + assert(op[1]->type->is_scalar()); + do_mul_mat_scalar(result, op[0], op[1]); + } + } else { + assert(op[1]->type->is_matrix()); + if (op[0]->type->is_vector()) { + do_mul_vec_mat(result, op[0], op[1]); + } else { + assert(op[0]->type->is_scalar()); + do_mul_mat_scalar(result, op[1], op[0]); + } + } + break; + + case ir_binop_all_equal: + case ir_binop_any_nequal: + do_equal_mat_mat(result, op[1], op[0], + (orig_expr->operation == ir_binop_all_equal)); + break; + + default: + printf("FINISHME: Handle matrix operation for %s\n", + orig_expr->operator_string()); + abort(); + } + orig_assign->remove(); + this->made_progress = true; + + return visit_continue; +} diff --git a/mesalib/src/glsl/main.cpp b/mesalib/src/glsl/main.cpp index 31f6cbad3..f0d951514 100644 --- a/mesalib/src/glsl/main.cpp +++ b/mesalib/src/glsl/main.cpp @@ -1,292 +1,292 @@ -/* - * Copyright © 2008, 2009 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 - -#ifdef _MSC_VER -#define __STDC__ 1 -#include -#define open _open -#define read _read -#define fstat _fstat -#define stat _stat -#define close _close -#define O_RDONLY _O_RDONLY -#endif - -#include "ast.h" -#include "glsl_parser_extras.h" -#include "ir_optimization.h" -#include "ir_print_visitor.h" -#include "program.h" -#include "loop_analysis.h" -#include "standalone_scaffolding.h" - -static void -initialize_context(struct gl_context *ctx, gl_api api) -{ - initialize_context_to_defaults(ctx, api); - - /* GLSL 1.30 isn't fully supported, but we need to advertise 1.30 so that - * the built-in functions for 1.30 can be built. - */ - ctx->Const.GLSLVersion = 130; - - ctx->Const.MaxClipPlanes = 8; - ctx->Const.MaxDrawBuffers = 2; - - /* More than the 1.10 minimum to appease parser tests taken from - * apps that (hopefully) already checked the number of coords. - */ - ctx->Const.MaxTextureCoordUnits = 4; - - ctx->Driver.NewShader = _mesa_new_shader; -} - -/* Returned string will have 'ctx' as its ralloc owner. */ -static char * -load_text_file(void *ctx, const char *file_name) -{ - char *text = NULL; - size_t size; - size_t total_read = 0; - FILE *fp = fopen(file_name, "rb"); - - if (!fp) { - return NULL; - } - - fseek(fp, 0L, SEEK_END); - size = ftell(fp); - fseek(fp, 0L, SEEK_SET); - - text = (char *) ralloc_size(ctx, size + 1); - if (text != NULL) { - do { - size_t bytes = fread(text + total_read, - 1, size - total_read, fp); - if (bytes < size - total_read) { - free(text); - text = NULL; - break; - } - - if (bytes == 0) { - break; - } - - total_read += bytes; - } while (total_read < size); - - text[total_read] = '\0'; - } - - fclose(fp); - - return text; -} - -int glsl_es = 0; -int dump_ast = 0; -int dump_hir = 0; -int dump_lir = 0; -int do_link = 0; - -const struct option compiler_opts[] = { - { "glsl-es", 0, &glsl_es, 1 }, - { "dump-ast", 0, &dump_ast, 1 }, - { "dump-hir", 0, &dump_hir, 1 }, - { "dump-lir", 0, &dump_lir, 1 }, - { "link", 0, &do_link, 1 }, - { NULL, 0, NULL, 0 } -}; - -/** - * \brief Print proper usage and exit with failure. - */ -void -usage_fail(const char *name) -{ - - const char *header = - "usage: %s [options] \n" - "\n" - "Possible options are:\n"; - printf(header, name, name); - for (const struct option *o = compiler_opts; o->name != 0; ++o) { - printf(" --%s\n", o->name); - } - exit(EXIT_FAILURE); -} - - -void -compile_shader(struct gl_context *ctx, struct gl_shader *shader) -{ - struct _mesa_glsl_parse_state *state = - new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); - - const char *source = shader->Source; - state->error = preprocess(state, &source, &state->info_log, - state->extensions, ctx->API) != 0; - - if (!state->error) { - _mesa_glsl_lexer_ctor(state, source); - _mesa_glsl_parse(state); - _mesa_glsl_lexer_dtor(state); - } - - if (dump_ast) { - foreach_list_const(n, &state->translation_unit) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf("\n\n"); - } - - shader->ir = new(shader) exec_list; - if (!state->error && !state->translation_unit.is_empty()) - _mesa_ast_to_hir(shader->ir, state); - - /* Print out the unoptimized IR. */ - if (!state->error && dump_hir) { - validate_ir_tree(shader->ir); - _mesa_print_ir(shader->ir, state); - } - - /* Optimization passes */ - if (!state->error && !shader->ir->is_empty()) { - bool progress; - do { - progress = do_common_optimization(shader->ir, false, 32); - } while (progress); - - validate_ir_tree(shader->ir); - } - - - /* Print out the resulting IR */ - if (!state->error && dump_lir) { - _mesa_print_ir(shader->ir, state); - } - - shader->symbols = state->symbols; - shader->CompileStatus = !state->error; - shader->Version = state->language_version; - memcpy(shader->builtins_to_link, state->builtins_to_link, - sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); - shader->num_builtins_to_link = state->num_builtins_to_link; - - if (shader->InfoLog) - ralloc_free(shader->InfoLog); - - shader->InfoLog = state->info_log; - - /* Retain any live IR, but trash the rest. */ - reparent_ir(shader->ir, shader); - - ralloc_free(state); - - return; -} - -int -main(int argc, char **argv) -{ - int status = EXIT_SUCCESS; - struct gl_context local_ctx; - struct gl_context *ctx = &local_ctx; - - int c; - int idx = 0; - while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1) - /* empty */ ; - - - if (argc <= optind) - usage_fail(argv[0]); - - initialize_context(ctx, (glsl_es) ? API_OPENGLES2 : API_OPENGL); - - struct gl_shader_program *whole_program; - - whole_program = rzalloc (NULL, struct gl_shader_program); - assert(whole_program != NULL); - whole_program->InfoLog = ralloc_strdup(whole_program, ""); - - for (/* empty */; argc > optind; optind++) { - whole_program->Shaders = - reralloc(whole_program, whole_program->Shaders, - struct gl_shader *, whole_program->NumShaders + 1); - assert(whole_program->Shaders != NULL); - - struct gl_shader *shader = rzalloc(whole_program, gl_shader); - - whole_program->Shaders[whole_program->NumShaders] = shader; - whole_program->NumShaders++; - - const unsigned len = strlen(argv[optind]); - if (len < 6) - usage_fail(argv[0]); - - const char *const ext = & argv[optind][len - 5]; - if (strncmp(".vert", ext, 5) == 0) - shader->Type = GL_VERTEX_SHADER; - else if (strncmp(".geom", ext, 5) == 0) - shader->Type = GL_GEOMETRY_SHADER; - else if (strncmp(".frag", ext, 5) == 0) - shader->Type = GL_FRAGMENT_SHADER; - else - usage_fail(argv[0]); - - shader->Source = load_text_file(whole_program, argv[optind]); - if (shader->Source == NULL) { - printf("File \"%s\" does not exist.\n", argv[optind]); - exit(EXIT_FAILURE); - } - - compile_shader(ctx, shader); - - if (!shader->CompileStatus) { - printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog); - status = EXIT_FAILURE; - break; - } - } - - if ((status == EXIT_SUCCESS) && do_link) { - link_shaders(ctx, whole_program); - status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE; - - if (strlen(whole_program->InfoLog) > 0) - printf("Info log for linking:\n%s\n", whole_program->InfoLog); - } - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) - ralloc_free(whole_program->_LinkedShaders[i]); - - ralloc_free(whole_program); - _mesa_glsl_release_types(); - _mesa_glsl_release_functions(); - - return status; -} +/* + * Copyright © 2008, 2009 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 + +#ifdef _MSC_VER +#define __STDC__ 1 +#include +#define open _open +#define read _read +#define fstat _fstat +#define stat _stat +#define close _close +#define O_RDONLY _O_RDONLY +#endif + +#include "ast.h" +#include "glsl_parser_extras.h" +#include "ir_optimization.h" +#include "ir_print_visitor.h" +#include "program.h" +#include "loop_analysis.h" +#include "standalone_scaffolding.h" + +static void +initialize_context(struct gl_context *ctx, gl_api api) +{ + initialize_context_to_defaults(ctx, api); + + /* GLSL 1.30 isn't fully supported, but we need to advertise 1.30 so that + * the built-in functions for 1.30 can be built. + */ + ctx->Const.GLSLVersion = 130; + + ctx->Const.MaxClipPlanes = 8; + ctx->Const.MaxDrawBuffers = 2; + + /* More than the 1.10 minimum to appease parser tests taken from + * apps that (hopefully) already checked the number of coords. + */ + ctx->Const.MaxTextureCoordUnits = 4; + + ctx->Driver.NewShader = _mesa_new_shader; +} + +/* Returned string will have 'ctx' as its ralloc owner. */ +static char * +load_text_file(void *ctx, const char *file_name) +{ + char *text = NULL; + size_t size; + size_t total_read = 0; + FILE *fp = fopen(file_name, "rb"); + + if (!fp) { + return NULL; + } + + fseek(fp, 0L, SEEK_END); + size = ftell(fp); + fseek(fp, 0L, SEEK_SET); + + text = (char *) ralloc_size(ctx, size + 1); + if (text != NULL) { + do { + size_t bytes = fread(text + total_read, + 1, size - total_read, fp); + if (bytes < size - total_read) { + free(text); + text = NULL; + break; + } + + if (bytes == 0) { + break; + } + + total_read += bytes; + } while (total_read < size); + + text[total_read] = '\0'; + } + + fclose(fp); + + return text; +} + +int glsl_es = 0; +int dump_ast = 0; +int dump_hir = 0; +int dump_lir = 0; +int do_link = 0; + +const struct option compiler_opts[] = { + { "glsl-es", 0, &glsl_es, 1 }, + { "dump-ast", 0, &dump_ast, 1 }, + { "dump-hir", 0, &dump_hir, 1 }, + { "dump-lir", 0, &dump_lir, 1 }, + { "link", 0, &do_link, 1 }, + { NULL, 0, NULL, 0 } +}; + +/** + * \brief Print proper usage and exit with failure. + */ +void +usage_fail(const char *name) +{ + + const char *header = + "usage: %s [options] \n" + "\n" + "Possible options are:\n"; + printf(header, name, name); + for (const struct option *o = compiler_opts; o->name != 0; ++o) { + printf(" --%s\n", o->name); + } + exit(EXIT_FAILURE); +} + + +void +compile_shader(struct gl_context *ctx, struct gl_shader *shader) +{ + struct _mesa_glsl_parse_state *state = + new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); + + const char *source = shader->Source; + state->error = preprocess(state, &source, &state->info_log, + state->extensions, ctx->API) != 0; + + if (!state->error) { + _mesa_glsl_lexer_ctor(state, source); + _mesa_glsl_parse(state); + _mesa_glsl_lexer_dtor(state); + } + + if (dump_ast) { + foreach_list_const(n, &state->translation_unit) { + ast_node *ast = exec_node_data(ast_node, n, link); + ast->print(); + } + printf("\n\n"); + } + + shader->ir = new(shader) exec_list; + if (!state->error && !state->translation_unit.is_empty()) + _mesa_ast_to_hir(shader->ir, state); + + /* Print out the unoptimized IR. */ + if (!state->error && dump_hir) { + validate_ir_tree(shader->ir); + _mesa_print_ir(shader->ir, state); + } + + /* Optimization passes */ + if (!state->error && !shader->ir->is_empty()) { + bool progress; + do { + progress = do_common_optimization(shader->ir, false, 32); + } while (progress); + + validate_ir_tree(shader->ir); + } + + + /* Print out the resulting IR */ + if (!state->error && dump_lir) { + _mesa_print_ir(shader->ir, state); + } + + shader->symbols = state->symbols; + shader->CompileStatus = !state->error; + shader->Version = state->language_version; + memcpy(shader->builtins_to_link, state->builtins_to_link, + sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); + shader->num_builtins_to_link = state->num_builtins_to_link; + + if (shader->InfoLog) + ralloc_free(shader->InfoLog); + + shader->InfoLog = state->info_log; + + /* Retain any live IR, but trash the rest. */ + reparent_ir(shader->ir, shader); + + ralloc_free(state); + + return; +} + +int +main(int argc, char **argv) +{ + int status = EXIT_SUCCESS; + struct gl_context local_ctx; + struct gl_context *ctx = &local_ctx; + + int c; + int idx = 0; + while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1) + /* empty */ ; + + + if (argc <= optind) + usage_fail(argv[0]); + + initialize_context(ctx, (glsl_es) ? API_OPENGLES2 : API_OPENGL); + + struct gl_shader_program *whole_program; + + whole_program = rzalloc (NULL, struct gl_shader_program); + assert(whole_program != NULL); + whole_program->InfoLog = ralloc_strdup(whole_program, ""); + + for (/* empty */; argc > optind; optind++) { + whole_program->Shaders = + reralloc(whole_program, whole_program->Shaders, + struct gl_shader *, whole_program->NumShaders + 1); + assert(whole_program->Shaders != NULL); + + struct gl_shader *shader = rzalloc(whole_program, gl_shader); + + whole_program->Shaders[whole_program->NumShaders] = shader; + whole_program->NumShaders++; + + const unsigned len = strlen(argv[optind]); + if (len < 6) + usage_fail(argv[0]); + + const char *const ext = & argv[optind][len - 5]; + if (strncmp(".vert", ext, 5) == 0) + shader->Type = GL_VERTEX_SHADER; + else if (strncmp(".geom", ext, 5) == 0) + shader->Type = GL_GEOMETRY_SHADER; + else if (strncmp(".frag", ext, 5) == 0) + shader->Type = GL_FRAGMENT_SHADER; + else + usage_fail(argv[0]); + + shader->Source = load_text_file(whole_program, argv[optind]); + if (shader->Source == NULL) { + printf("File \"%s\" does not exist.\n", argv[optind]); + exit(EXIT_FAILURE); + } + + compile_shader(ctx, shader); + + if (!shader->CompileStatus) { + printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog); + status = EXIT_FAILURE; + break; + } + } + + if ((status == EXIT_SUCCESS) && do_link) { + link_shaders(ctx, whole_program); + status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE; + + if (strlen(whole_program->InfoLog) > 0) + printf("Info log for linking:\n%s\n", whole_program->InfoLog); + } + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) + ralloc_free(whole_program->_LinkedShaders[i]); + + ralloc_free(whole_program); + _mesa_glsl_release_types(); + _mesa_glsl_release_functions(); + + return status; +} diff --git a/mesalib/src/glsl/opt_constant_propagation.cpp b/mesalib/src/glsl/opt_constant_propagation.cpp index 7c42b6191..af77e4906 100644 --- a/mesalib/src/glsl/opt_constant_propagation.cpp +++ b/mesalib/src/glsl/opt_constant_propagation.cpp @@ -1,445 +1,445 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * constant of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, constant, 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 constantright 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 CONSTANTRIGHT 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. - */ - -/** - * \file opt_constant_propagation.cpp - * - * Tracks assignments of constants to channels of variables, and - * usage of those constant channels with direct usage of the constants. - * - * This can lead to constant folding and algebraic optimizations in - * those later expressions, while causing no increase in instruction - * count (due to constants being generally free to load from a - * constant push buffer or as instruction immediate values) and - * possibly reducing register pressure. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_rvalue_visitor.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -class acp_entry : public exec_node -{ -public: - acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant) - { - assert(var); - assert(constant); - this->var = var; - this->write_mask = write_mask; - this->constant = constant; - this->initial_values = write_mask; - } - - acp_entry(const acp_entry *src) - { - this->var = src->var; - this->write_mask = src->write_mask; - this->constant = src->constant; - this->initial_values = src->initial_values; - } - - ir_variable *var; - ir_constant *constant; - unsigned write_mask; - - /** Mask of values initially available in the constant. */ - unsigned initial_values; -}; - - -class kill_entry : public exec_node -{ -public: - kill_entry(ir_variable *var, unsigned write_mask) - { - assert(var); - this->var = var; - this->write_mask = write_mask; - } - - ir_variable *var; - unsigned write_mask; -}; - -class ir_constant_propagation_visitor : public ir_rvalue_visitor { -public: - ir_constant_propagation_visitor() - { - progress = false; - mem_ctx = ralloc_context(0); - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - } - ~ir_constant_propagation_visitor() - { - ralloc_free(mem_ctx); - } - - virtual ir_visitor_status visit_enter(class ir_loop *); - virtual ir_visitor_status visit_enter(class ir_function_signature *); - virtual ir_visitor_status visit_enter(class ir_function *); - virtual ir_visitor_status visit_leave(class ir_assignment *); - virtual ir_visitor_status visit_enter(class ir_call *); - virtual ir_visitor_status visit_enter(class ir_if *); - - void add_constant(ir_assignment *ir); - void kill(ir_variable *ir, unsigned write_mask); - void handle_if_block(exec_list *instructions); - void handle_rvalue(ir_rvalue **rvalue); - - /** List of acp_entry: The available constants to propagate */ - exec_list *acp; - - /** - * List of kill_entry: The masks of variables whose values were - * killed in this block. - */ - exec_list *kills; - - bool progress; - - bool killed_all; - - void *mem_ctx; -}; - - -void -ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue) -{ - if (this->in_assignee || !*rvalue) - return; - - const glsl_type *type = (*rvalue)->type; - if (!type->is_scalar() && !type->is_vector()) - return; - - ir_swizzle *swiz = NULL; - ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); - if (!deref) { - swiz = (*rvalue)->as_swizzle(); - if (!swiz) - return; - - deref = swiz->val->as_dereference_variable(); - if (!deref) - return; - } - - ir_constant_data data; - memset(&data, 0, sizeof(data)); - - for (unsigned int i = 0; i < type->components(); i++) { - int channel; - acp_entry *found = NULL; - - if (swiz) { - switch (i) { - case 0: channel = swiz->mask.x; break; - case 1: channel = swiz->mask.y; break; - case 2: channel = swiz->mask.z; break; - case 3: channel = swiz->mask.w; break; - default: assert(!"shouldn't be reached"); channel = 0; break; - } - } else { - channel = i; - } - - foreach_iter(exec_list_iterator, iter, *this->acp) { - acp_entry *entry = (acp_entry *)iter.get(); - if (entry->var == deref->var && entry->write_mask & (1 << channel)) { - found = entry; - break; - } - } - - if (!found) - return; - - int rhs_channel = 0; - for (int j = 0; j < 4; j++) { - if (j == channel) - break; - if (found->initial_values & (1 << j)) - rhs_channel++; - } - - switch (type->base_type) { - case GLSL_TYPE_FLOAT: - data.f[i] = found->constant->value.f[rhs_channel]; - break; - case GLSL_TYPE_INT: - data.i[i] = found->constant->value.i[rhs_channel]; - break; - case GLSL_TYPE_UINT: - data.u[i] = found->constant->value.u[rhs_channel]; - break; - case GLSL_TYPE_BOOL: - data.b[i] = found->constant->value.b[rhs_channel]; - break; - default: - assert(!"not reached"); - break; - } - } - - *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data); - this->progress = true; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir) -{ - /* Treat entry into a function signature as a completely separate - * block. Any instructions at global scope will be shuffled into - * main() at link time, so they're irrelevant to us. - */ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body); - - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = orig_killed_all; - - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_leave(ir_assignment *ir) -{ - if (this->in_assignee) - return visit_continue; - - kill(ir->lhs->variable_referenced(), ir->write_mask); - - add_constant(ir); - - return visit_continue; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_function *ir) -{ - (void) ir; - return visit_continue; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_call *ir) -{ - /* Do constant propagation on call parameters, but skip any out params */ - exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { - ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); - ir_rvalue *param = (ir_rvalue *)iter.get(); - if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { - ir_rvalue *new_param = param; - handle_rvalue(&new_param); - if (new_param != param) - param->replace_with(new_param); - else - param->accept(this); - } - sig_param_iter.next(); - } - - /* Since we're unlinked, we don't (necssarily) know the side effects of - * this call. So kill all copies. - */ - acp->make_empty(); - this->killed_all = true; - - return visit_continue_with_parent; -} - -void -ir_constant_propagation_visitor::handle_if_block(exec_list *instructions) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - /* Populate the initial acp with a constant of the original */ - foreach_iter(exec_list_iterator, iter, *orig_acp) { - acp_entry *a = (acp_entry *)iter.get(); - this->acp->push_tail(new(this->mem_ctx) acp_entry(a)); - } - - visit_list_elements(this, instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - foreach_iter(exec_list_iterator, iter, *new_kills) { - kill_entry *k = (kill_entry *)iter.get(); - kill(k->var, k->write_mask); - } -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_if *ir) -{ - ir->condition->accept(this); - handle_rvalue(&ir->condition); - - handle_if_block(&ir->then_instructions); - handle_if_block(&ir->else_instructions); - - /* handle_if_block() already descended into the children. */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_loop *ir) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - /* FINISHME: For now, the initial acp for loops is totally empty. - * We could go through once, then go through again with the acp - * cloned minus the killed entries after the first run through. - */ - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body_instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - foreach_iter(exec_list_iterator, iter, *new_kills) { - kill_entry *k = (kill_entry *)iter.get(); - kill(k->var, k->write_mask); - } - - /* already descended into the children. */ - return visit_continue_with_parent; -} - -void -ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask) -{ - assert(var != NULL); - - /* We don't track non-vectors. */ - if (!var->type->is_vector() && !var->type->is_scalar()) - return; - - /* Remove any entries currently in the ACP for this kill. */ - foreach_iter(exec_list_iterator, iter, *this->acp) { - acp_entry *entry = (acp_entry *)iter.get(); - - if (entry->var == var) { - entry->write_mask &= ~write_mask; - if (entry->write_mask == 0) - entry->remove(); - } - } - - /* Add this writemask of the variable to the list of killed - * variables in this block. - */ - foreach_iter(exec_list_iterator, iter, *this->kills) { - kill_entry *entry = (kill_entry *)iter.get(); - - if (entry->var == var) { - entry->write_mask |= write_mask; - return; - } - } - /* Not already in the list. Make new entry. */ - this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask)); -} - -/** - * Adds an entry to the available constant list if it's a plain assignment - * of a variable to a variable. - */ -void -ir_constant_propagation_visitor::add_constant(ir_assignment *ir) -{ - acp_entry *entry; - - if (ir->condition) - return; - - if (!ir->write_mask) - return; - - ir_dereference_variable *deref = ir->lhs->as_dereference_variable(); - ir_constant *constant = ir->rhs->as_constant(); - - if (!deref || !constant) - return; - - /* Only do constant propagation on vectors. Constant matrices, - * arrays, or structures would require more work elsewhere. - */ - if (!deref->var->type->is_vector() && !deref->var->type->is_scalar()) - return; - - entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant); - this->acp->push_tail(entry); -} - -/** - * Does a constant propagation pass on the code present in the instruction stream. - */ -bool -do_constant_propagation(exec_list *instructions) -{ - ir_constant_propagation_visitor v; - - visit_list_elements(&v, instructions); - - return v.progress; -} +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * constant of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, constant, 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 constantright 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 CONSTANTRIGHT 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. + */ + +/** + * \file opt_constant_propagation.cpp + * + * Tracks assignments of constants to channels of variables, and + * usage of those constant channels with direct usage of the constants. + * + * This can lead to constant folding and algebraic optimizations in + * those later expressions, while causing no increase in instruction + * count (due to constants being generally free to load from a + * constant push buffer or as instruction immediate values) and + * possibly reducing register pressure. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_rvalue_visitor.h" +#include "ir_basic_block.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +class acp_entry : public exec_node +{ +public: + acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant) + { + assert(var); + assert(constant); + this->var = var; + this->write_mask = write_mask; + this->constant = constant; + this->initial_values = write_mask; + } + + acp_entry(const acp_entry *src) + { + this->var = src->var; + this->write_mask = src->write_mask; + this->constant = src->constant; + this->initial_values = src->initial_values; + } + + ir_variable *var; + ir_constant *constant; + unsigned write_mask; + + /** Mask of values initially available in the constant. */ + unsigned initial_values; +}; + + +class kill_entry : public exec_node +{ +public: + kill_entry(ir_variable *var, unsigned write_mask) + { + assert(var); + this->var = var; + this->write_mask = write_mask; + } + + ir_variable *var; + unsigned write_mask; +}; + +class ir_constant_propagation_visitor : public ir_rvalue_visitor { +public: + ir_constant_propagation_visitor() + { + progress = false; + mem_ctx = ralloc_context(0); + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + } + ~ir_constant_propagation_visitor() + { + ralloc_free(mem_ctx); + } + + virtual ir_visitor_status visit_enter(class ir_loop *); + virtual ir_visitor_status visit_enter(class ir_function_signature *); + virtual ir_visitor_status visit_enter(class ir_function *); + virtual ir_visitor_status visit_leave(class ir_assignment *); + virtual ir_visitor_status visit_enter(class ir_call *); + virtual ir_visitor_status visit_enter(class ir_if *); + + void add_constant(ir_assignment *ir); + void kill(ir_variable *ir, unsigned write_mask); + void handle_if_block(exec_list *instructions); + void handle_rvalue(ir_rvalue **rvalue); + + /** List of acp_entry: The available constants to propagate */ + exec_list *acp; + + /** + * List of kill_entry: The masks of variables whose values were + * killed in this block. + */ + exec_list *kills; + + bool progress; + + bool killed_all; + + void *mem_ctx; +}; + + +void +ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue) +{ + if (this->in_assignee || !*rvalue) + return; + + const glsl_type *type = (*rvalue)->type; + if (!type->is_scalar() && !type->is_vector()) + return; + + ir_swizzle *swiz = NULL; + ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); + if (!deref) { + swiz = (*rvalue)->as_swizzle(); + if (!swiz) + return; + + deref = swiz->val->as_dereference_variable(); + if (!deref) + return; + } + + ir_constant_data data; + memset(&data, 0, sizeof(data)); + + for (unsigned int i = 0; i < type->components(); i++) { + int channel; + acp_entry *found = NULL; + + if (swiz) { + switch (i) { + case 0: channel = swiz->mask.x; break; + case 1: channel = swiz->mask.y; break; + case 2: channel = swiz->mask.z; break; + case 3: channel = swiz->mask.w; break; + default: assert(!"shouldn't be reached"); channel = 0; break; + } + } else { + channel = i; + } + + foreach_iter(exec_list_iterator, iter, *this->acp) { + acp_entry *entry = (acp_entry *)iter.get(); + if (entry->var == deref->var && entry->write_mask & (1 << channel)) { + found = entry; + break; + } + } + + if (!found) + return; + + int rhs_channel = 0; + for (int j = 0; j < 4; j++) { + if (j == channel) + break; + if (found->initial_values & (1 << j)) + rhs_channel++; + } + + switch (type->base_type) { + case GLSL_TYPE_FLOAT: + data.f[i] = found->constant->value.f[rhs_channel]; + break; + case GLSL_TYPE_INT: + data.i[i] = found->constant->value.i[rhs_channel]; + break; + case GLSL_TYPE_UINT: + data.u[i] = found->constant->value.u[rhs_channel]; + break; + case GLSL_TYPE_BOOL: + data.b[i] = found->constant->value.b[rhs_channel]; + break; + default: + assert(!"not reached"); + break; + } + } + + *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data); + this->progress = true; +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir) +{ + /* Treat entry into a function signature as a completely separate + * block. Any instructions at global scope will be shuffled into + * main() at link time, so they're irrelevant to us. + */ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body); + + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = orig_killed_all; + + return visit_continue_with_parent; +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_leave(ir_assignment *ir) +{ + if (this->in_assignee) + return visit_continue; + + kill(ir->lhs->variable_referenced(), ir->write_mask); + + add_constant(ir); + + return visit_continue; +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_enter(ir_function *ir) +{ + (void) ir; + return visit_continue; +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_enter(ir_call *ir) +{ + /* Do constant propagation on call parameters, but skip any out params */ + exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { + ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); + ir_rvalue *param = (ir_rvalue *)iter.get(); + if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { + ir_rvalue *new_param = param; + handle_rvalue(&new_param); + if (new_param != param) + param->replace_with(new_param); + else + param->accept(this); + } + sig_param_iter.next(); + } + + /* Since we're unlinked, we don't (necssarily) know the side effects of + * this call. So kill all copies. + */ + acp->make_empty(); + this->killed_all = true; + + return visit_continue_with_parent; +} + +void +ir_constant_propagation_visitor::handle_if_block(exec_list *instructions) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + /* Populate the initial acp with a constant of the original */ + foreach_iter(exec_list_iterator, iter, *orig_acp) { + acp_entry *a = (acp_entry *)iter.get(); + this->acp->push_tail(new(this->mem_ctx) acp_entry(a)); + } + + visit_list_elements(this, instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + foreach_iter(exec_list_iterator, iter, *new_kills) { + kill_entry *k = (kill_entry *)iter.get(); + kill(k->var, k->write_mask); + } +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_enter(ir_if *ir) +{ + ir->condition->accept(this); + handle_rvalue(&ir->condition); + + handle_if_block(&ir->then_instructions); + handle_if_block(&ir->else_instructions); + + /* handle_if_block() already descended into the children. */ + return visit_continue_with_parent; +} + +ir_visitor_status +ir_constant_propagation_visitor::visit_enter(ir_loop *ir) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + /* FINISHME: For now, the initial acp for loops is totally empty. + * We could go through once, then go through again with the acp + * cloned minus the killed entries after the first run through. + */ + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body_instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + foreach_iter(exec_list_iterator, iter, *new_kills) { + kill_entry *k = (kill_entry *)iter.get(); + kill(k->var, k->write_mask); + } + + /* already descended into the children. */ + return visit_continue_with_parent; +} + +void +ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask) +{ + assert(var != NULL); + + /* We don't track non-vectors. */ + if (!var->type->is_vector() && !var->type->is_scalar()) + return; + + /* Remove any entries currently in the ACP for this kill. */ + foreach_iter(exec_list_iterator, iter, *this->acp) { + acp_entry *entry = (acp_entry *)iter.get(); + + if (entry->var == var) { + entry->write_mask &= ~write_mask; + if (entry->write_mask == 0) + entry->remove(); + } + } + + /* Add this writemask of the variable to the list of killed + * variables in this block. + */ + foreach_iter(exec_list_iterator, iter, *this->kills) { + kill_entry *entry = (kill_entry *)iter.get(); + + if (entry->var == var) { + entry->write_mask |= write_mask; + return; + } + } + /* Not already in the list. Make new entry. */ + this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask)); +} + +/** + * Adds an entry to the available constant list if it's a plain assignment + * of a variable to a variable. + */ +void +ir_constant_propagation_visitor::add_constant(ir_assignment *ir) +{ + acp_entry *entry; + + if (ir->condition) + return; + + if (!ir->write_mask) + return; + + ir_dereference_variable *deref = ir->lhs->as_dereference_variable(); + ir_constant *constant = ir->rhs->as_constant(); + + if (!deref || !constant) + return; + + /* Only do constant propagation on vectors. Constant matrices, + * arrays, or structures would require more work elsewhere. + */ + if (!deref->var->type->is_vector() && !deref->var->type->is_scalar()) + return; + + entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant); + this->acp->push_tail(entry); +} + +/** + * Does a constant propagation pass on the code present in the instruction stream. + */ +bool +do_constant_propagation(exec_list *instructions) +{ + ir_constant_propagation_visitor v; + + visit_list_elements(&v, instructions); + + return v.progress; +} diff --git a/mesalib/src/glsl/opt_constant_variable.cpp b/mesalib/src/glsl/opt_constant_variable.cpp index f1aeaa99a..3fa7c3bad 100644 --- a/mesalib/src/glsl/opt_constant_variable.cpp +++ b/mesalib/src/glsl/opt_constant_variable.cpp @@ -1,195 +1,195 @@ -/* - * 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. - */ - -/** - * \file opt_constant_variable.cpp - * - * Marks variables assigned a single constant value over the course - * of the program as constant. - * - * The goal here is to trigger further constant folding and then dead - * code elimination. This is common with vector/matrix constructors - * and calls to builtin functions. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -struct assignment_entry { - exec_node link; - int assignment_count; - ir_variable *var; - ir_constant *constval; - bool our_scope; -}; - -class ir_constant_variable_visitor : public ir_hierarchical_visitor { -public: - virtual ir_visitor_status visit_enter(ir_dereference_variable *); - virtual ir_visitor_status visit(ir_variable *); - virtual ir_visitor_status visit_enter(ir_assignment *); - virtual ir_visitor_status visit_enter(ir_call *); - - exec_list list; -}; - -static struct assignment_entry * -get_assignment_entry(ir_variable *var, exec_list *list) -{ - struct assignment_entry *entry; - - foreach_list_typed(struct assignment_entry, entry, link, list) { - if (entry->var == var) - return entry; - } - - entry = (struct assignment_entry *)calloc(1, sizeof(*entry)); - entry->var = var; - list->push_head(&entry->link); - return entry; -} - -ir_visitor_status -ir_constant_variable_visitor::visit(ir_variable *ir) -{ - struct assignment_entry *entry = get_assignment_entry(ir, &this->list); - entry->our_scope = true; - return visit_continue; -} - -/* Skip derefs of variables so that we can detect declarations. */ -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir) -{ - (void)ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_assignment *ir) -{ - ir_constant *constval; - struct assignment_entry *entry; - - entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list); - assert(entry); - entry->assignment_count++; - - /* If it's already constant, don't do the work. */ - if (entry->var->constant_value) - return visit_continue; - - /* OK, now find if we actually have all the right conditions for - * this to be a constant value assigned to the var. - */ - if (ir->condition) - return visit_continue; - - ir_variable *var = ir->whole_variable_written(); - if (!var) - return visit_continue; - - constval = ir->rhs->constant_expression_value(); - if (!constval) - return visit_continue; - - /* Mark this entry as having a constant assignment (if the - * assignment count doesn't go >1). do_constant_variable will fix - * up the variable with the constant value later. - */ - entry->constval = constval; - - return visit_continue; -} - -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_call *ir) -{ - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *param = (ir_variable *)sig_iter.get(); - - if (param->mode == ir_var_out || - param->mode == ir_var_inout) { - ir_variable *var = param_rval->variable_referenced(); - struct assignment_entry *entry; - - assert(var); - entry = get_assignment_entry(var, &this->list); - entry->assignment_count++; - } - sig_iter.next(); - } - return visit_continue; -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_constant_variable(exec_list *instructions) -{ - bool progress = false; - ir_constant_variable_visitor v; - - v.run(instructions); - - while (!v.list.is_empty()) { - - struct assignment_entry *entry; - entry = exec_node_data(struct assignment_entry, v.list.head, link); - - if (entry->assignment_count == 1 && entry->constval && entry->our_scope) { - entry->var->constant_value = entry->constval; - progress = true; - } - entry->link.remove(); - free(entry); - } - - return progress; -} - -bool -do_constant_variable_unlinked(exec_list *instructions) -{ - bool progress = false; - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_function *f = ir->as_function(); - if (f) { - foreach_iter(exec_list_iterator, sigiter, *f) { - ir_function_signature *sig = - (ir_function_signature *) sigiter.get(); - if (do_constant_variable(&sig->body)) - progress = true; - } - } - } - - return progress; -} +/* + * 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. + */ + +/** + * \file opt_constant_variable.cpp + * + * Marks variables assigned a single constant value over the course + * of the program as constant. + * + * The goal here is to trigger further constant folding and then dead + * code elimination. This is common with vector/matrix constructors + * and calls to builtin functions. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +struct assignment_entry { + exec_node link; + int assignment_count; + ir_variable *var; + ir_constant *constval; + bool our_scope; +}; + +class ir_constant_variable_visitor : public ir_hierarchical_visitor { +public: + virtual ir_visitor_status visit_enter(ir_dereference_variable *); + virtual ir_visitor_status visit(ir_variable *); + virtual ir_visitor_status visit_enter(ir_assignment *); + virtual ir_visitor_status visit_enter(ir_call *); + + exec_list list; +}; + +static struct assignment_entry * +get_assignment_entry(ir_variable *var, exec_list *list) +{ + struct assignment_entry *entry; + + foreach_list_typed(struct assignment_entry, entry, link, list) { + if (entry->var == var) + return entry; + } + + entry = (struct assignment_entry *)calloc(1, sizeof(*entry)); + entry->var = var; + list->push_head(&entry->link); + return entry; +} + +ir_visitor_status +ir_constant_variable_visitor::visit(ir_variable *ir) +{ + struct assignment_entry *entry = get_assignment_entry(ir, &this->list); + entry->our_scope = true; + return visit_continue; +} + +/* Skip derefs of variables so that we can detect declarations. */ +ir_visitor_status +ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir) +{ + (void)ir; + return visit_continue_with_parent; +} + +ir_visitor_status +ir_constant_variable_visitor::visit_enter(ir_assignment *ir) +{ + ir_constant *constval; + struct assignment_entry *entry; + + entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list); + assert(entry); + entry->assignment_count++; + + /* If it's already constant, don't do the work. */ + if (entry->var->constant_value) + return visit_continue; + + /* OK, now find if we actually have all the right conditions for + * this to be a constant value assigned to the var. + */ + if (ir->condition) + return visit_continue; + + ir_variable *var = ir->whole_variable_written(); + if (!var) + return visit_continue; + + constval = ir->rhs->constant_expression_value(); + if (!constval) + return visit_continue; + + /* Mark this entry as having a constant assignment (if the + * assignment count doesn't go >1). do_constant_variable will fix + * up the variable with the constant value later. + */ + entry->constval = constval; + + return visit_continue; +} + +ir_visitor_status +ir_constant_variable_visitor::visit_enter(ir_call *ir) +{ + exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param_rval = (ir_rvalue *)iter.get(); + ir_variable *param = (ir_variable *)sig_iter.get(); + + if (param->mode == ir_var_out || + param->mode == ir_var_inout) { + ir_variable *var = param_rval->variable_referenced(); + struct assignment_entry *entry; + + assert(var); + entry = get_assignment_entry(var, &this->list); + entry->assignment_count++; + } + sig_iter.next(); + } + return visit_continue; +} + +/** + * Does a copy propagation pass on the code present in the instruction stream. + */ +bool +do_constant_variable(exec_list *instructions) +{ + bool progress = false; + ir_constant_variable_visitor v; + + v.run(instructions); + + while (!v.list.is_empty()) { + + struct assignment_entry *entry; + entry = exec_node_data(struct assignment_entry, v.list.head, link); + + if (entry->assignment_count == 1 && entry->constval && entry->our_scope) { + entry->var->constant_value = entry->constval; + progress = true; + } + entry->link.remove(); + free(entry); + } + + return progress; +} + +bool +do_constant_variable_unlinked(exec_list *instructions) +{ + bool progress = false; + + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_function *f = ir->as_function(); + if (f) { + foreach_iter(exec_list_iterator, sigiter, *f) { + ir_function_signature *sig = + (ir_function_signature *) sigiter.get(); + if (do_constant_variable(&sig->body)) + progress = true; + } + } + } + + return progress; +} diff --git a/mesalib/src/glsl/opt_copy_propagation_elements.cpp b/mesalib/src/glsl/opt_copy_propagation_elements.cpp index 580c10f27..a91e624cb 100644 --- a/mesalib/src/glsl/opt_copy_propagation_elements.cpp +++ b/mesalib/src/glsl/opt_copy_propagation_elements.cpp @@ -1,467 +1,467 @@ -/* - * 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. - */ - -/** - * \file opt_copy_propagation_elements.cpp - * - * Replaces usage of recently-copied components of variables with the - * previous copy of the variable. - * - * This pass can be compared with opt_copy_propagation, which operands - * on arbitrary whole-variable copies. However, in order to handle - * the copy propagation of swizzled variables or writemasked writes, - * we want to track things on a channel-wise basis. I found that - * trying to mix the swizzled/writemasked support here with the - * whole-variable stuff in opt_copy_propagation.cpp just made a mess, - * so this is separate despite the ACP handling being somewhat - * similar. - * - * This should reduce the number of MOV instructions in the generated - * programs unless copy propagation is also done on the LIR, and may - * help anyway by triggering other optimizations that live in the HIR. - */ - -#include "ir.h" -#include "ir_rvalue_visitor.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -static bool debug = false; - -class acp_entry : public exec_node -{ -public: - acp_entry(ir_variable *lhs, ir_variable *rhs, int write_mask, int swizzle[4]) - { - this->lhs = lhs; - this->rhs = rhs; - this->write_mask = write_mask; - memcpy(this->swizzle, swizzle, sizeof(this->swizzle)); - } - - acp_entry(acp_entry *a) - { - this->lhs = a->lhs; - this->rhs = a->rhs; - this->write_mask = a->write_mask; - memcpy(this->swizzle, a->swizzle, sizeof(this->swizzle)); - } - - ir_variable *lhs; - ir_variable *rhs; - unsigned int write_mask; - int swizzle[4]; -}; - - -class kill_entry : public exec_node -{ -public: - kill_entry(ir_variable *var, int write_mask) - { - this->var = var; - this->write_mask = write_mask; - } - - ir_variable *var; - unsigned int write_mask; -}; - -class ir_copy_propagation_elements_visitor : public ir_rvalue_visitor { -public: - ir_copy_propagation_elements_visitor() - { - this->progress = false; - this->mem_ctx = ralloc_context(NULL); - this->shader_mem_ctx = NULL; - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - } - ~ir_copy_propagation_elements_visitor() - { - ralloc_free(mem_ctx); - } - - virtual ir_visitor_status visit_enter(class ir_loop *); - virtual ir_visitor_status visit_enter(class ir_function_signature *); - virtual ir_visitor_status visit_leave(class ir_assignment *); - virtual ir_visitor_status visit_enter(class ir_call *); - virtual ir_visitor_status visit_enter(class ir_if *); - - void handle_rvalue(ir_rvalue **rvalue); - - void add_copy(ir_assignment *ir); - void kill(kill_entry *k); - void handle_if_block(exec_list *instructions); - - /** List of acp_entry: The available copies to propagate */ - exec_list *acp; - /** - * List of kill_entry: The variables whose values were killed in this - * block. - */ - exec_list *kills; - - bool progress; - - bool killed_all; - - /* Context for our local data structures. */ - void *mem_ctx; - /* Context for allocating new shader nodes. */ - void *shader_mem_ctx; -}; - -ir_visitor_status -ir_copy_propagation_elements_visitor::visit_enter(ir_function_signature *ir) -{ - /* Treat entry into a function signature as a completely separate - * block. Any instructions at global scope will be shuffled into - * main() at link time, so they're irrelevant to us. - */ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body); - - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = orig_killed_all; - - return visit_continue_with_parent; -} - -ir_visitor_status -ir_copy_propagation_elements_visitor::visit_leave(ir_assignment *ir) -{ - ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); - ir_variable *var = ir->lhs->variable_referenced(); - - if (var->type->is_scalar() || var->type->is_vector()) { - kill_entry *k; - - if (lhs) - k = new(mem_ctx) kill_entry(var, ir->write_mask); - else - k = new(mem_ctx) kill_entry(var, ~0); - - kill(k); - } - - add_copy(ir); - - return visit_continue; -} - -/** - * Replaces dereferences of ACP RHS variables with ACP LHS variables. - * - * This is where the actual copy propagation occurs. Note that the - * rewriting of ir_dereference means that the ir_dereference instance - * must not be shared by multiple IR operations! - */ -void -ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir) -{ - int swizzle_chan[4]; - ir_dereference_variable *deref_var; - ir_variable *source[4] = {NULL, NULL, NULL, NULL}; - int source_chan[4]; - int chans; - - if (!*ir) - return; - - ir_swizzle *swizzle = (*ir)->as_swizzle(); - if (swizzle) { - deref_var = swizzle->val->as_dereference_variable(); - if (!deref_var) - return; - - swizzle_chan[0] = swizzle->mask.x; - swizzle_chan[1] = swizzle->mask.y; - swizzle_chan[2] = swizzle->mask.z; - swizzle_chan[3] = swizzle->mask.w; - chans = swizzle->type->vector_elements; - } else { - deref_var = (*ir)->as_dereference_variable(); - if (!deref_var) - return; - - swizzle_chan[0] = 0; - swizzle_chan[1] = 1; - swizzle_chan[2] = 2; - swizzle_chan[3] = 3; - chans = deref_var->type->vector_elements; - } - - if (this->in_assignee) - return; - - ir_variable *var = deref_var->var; - - /* Try to find ACP entries covering swizzle_chan[], hoping they're - * the same source variable. - */ - foreach_iter(exec_list_iterator, iter, *this->acp) { - acp_entry *entry = (acp_entry *)iter.get(); - - if (var == entry->lhs) { - for (int c = 0; c < chans; c++) { - if (entry->write_mask & (1 << swizzle_chan[c])) { - source[c] = entry->rhs; - source_chan[c] = entry->swizzle[swizzle_chan[c]]; - } - } - } - } - - /* Make sure all channels are copying from the same source variable. */ - if (!source[0]) - return; - for (int c = 1; c < chans; c++) { - if (source[c] != source[0]) - return; - } - - if (!shader_mem_ctx) - shader_mem_ctx = ralloc_parent(deref_var); - - if (debug) { - printf("Copy propagation from:\n"); - (*ir)->print(); - } - - deref_var = new(shader_mem_ctx) ir_dereference_variable(source[0]); - *ir = new(shader_mem_ctx) ir_swizzle(deref_var, - source_chan[0], - source_chan[1], - source_chan[2], - source_chan[3], - chans); - - if (debug) { - printf("to:\n"); - (*ir)->print(); - printf("\n"); - } -} - - -ir_visitor_status -ir_copy_propagation_elements_visitor::visit_enter(ir_call *ir) -{ - /* Do copy propagation on call parameters, but skip any out params */ - exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { - ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); - ir_instruction *ir = (ir_instruction *)iter.get(); - if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { - ir->accept(this); - } - sig_param_iter.next(); - } - - /* Since we're unlinked, we don't (necessarily) know the side effects of - * this call. So kill all copies. - */ - acp->make_empty(); - this->killed_all = true; - - return visit_continue_with_parent; -} - -void -ir_copy_propagation_elements_visitor::handle_if_block(exec_list *instructions) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - /* Populate the initial acp with a copy of the original */ - foreach_iter(exec_list_iterator, iter, *orig_acp) { - acp_entry *a = (acp_entry *)iter.get(); - this->acp->push_tail(new(this->mem_ctx) acp_entry(a)); - } - - visit_list_elements(this, instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - /* Move the new kills into the parent block's list, removing them - * from the parent's ACP list in the process. - */ - foreach_list_safe(node, new_kills) { - kill_entry *k = (kill_entry *)node; - kill(k); - } -} - -ir_visitor_status -ir_copy_propagation_elements_visitor::visit_enter(ir_if *ir) -{ - ir->condition->accept(this); - - handle_if_block(&ir->then_instructions); - handle_if_block(&ir->else_instructions); - - /* handle_if_block() already descended into the children. */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_copy_propagation_elements_visitor::visit_enter(ir_loop *ir) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - /* FINISHME: For now, the initial acp for loops is totally empty. - * We could go through once, then go through again with the acp - * cloned minus the killed entries after the first run through. - */ - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body_instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - foreach_list_safe(node, new_kills) { - kill_entry *k = (kill_entry *)node; - kill(k); - } - - /* already descended into the children. */ - return visit_continue_with_parent; -} - -/* Remove any entries currently in the ACP for this kill. */ -void -ir_copy_propagation_elements_visitor::kill(kill_entry *k) -{ - foreach_list_safe(node, acp) { - acp_entry *entry = (acp_entry *)node; - - if (entry->lhs == k->var) { - entry->write_mask = entry->write_mask & ~k->write_mask; - if (entry->write_mask == 0) { - entry->remove(); - continue; - } - } - if (entry->rhs == k->var) { - entry->remove(); - } - } - - /* If we were on a list, remove ourselves before inserting */ - if (k->next) - k->remove(); - - this->kills->push_tail(k); -} - -/** - * Adds directly-copied channels between vector variables to the available - * copy propagation list. - */ -void -ir_copy_propagation_elements_visitor::add_copy(ir_assignment *ir) -{ - acp_entry *entry; - int orig_swizzle[4] = {0, 1, 2, 3}; - int swizzle[4]; - - if (ir->condition) - return; - - ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); - if (!lhs || !(lhs->type->is_scalar() || lhs->type->is_vector())) - return; - - ir_dereference_variable *rhs = ir->rhs->as_dereference_variable(); - if (!rhs) { - ir_swizzle *swiz = ir->rhs->as_swizzle(); - if (!swiz) - return; - - rhs = swiz->val->as_dereference_variable(); - if (!rhs) - return; - - orig_swizzle[0] = swiz->mask.x; - orig_swizzle[1] = swiz->mask.y; - orig_swizzle[2] = swiz->mask.z; - orig_swizzle[3] = swiz->mask.w; - } - - /* Move the swizzle channels out to the positions they match in the - * destination. We don't want to have to rewrite the swizzle[] - * array every time we clear a bit of the write_mask. - */ - int j = 0; - for (int i = 0; i < 4; i++) { - if (ir->write_mask & (1 << i)) - swizzle[i] = orig_swizzle[j++]; - } - - entry = new(this->mem_ctx) acp_entry(lhs->var, rhs->var, ir->write_mask, - swizzle); - this->acp->push_tail(entry); -} - -bool -do_copy_propagation_elements(exec_list *instructions) -{ - ir_copy_propagation_elements_visitor v; - - visit_list_elements(&v, instructions); - - return v.progress; -} +/* + * 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. + */ + +/** + * \file opt_copy_propagation_elements.cpp + * + * Replaces usage of recently-copied components of variables with the + * previous copy of the variable. + * + * This pass can be compared with opt_copy_propagation, which operands + * on arbitrary whole-variable copies. However, in order to handle + * the copy propagation of swizzled variables or writemasked writes, + * we want to track things on a channel-wise basis. I found that + * trying to mix the swizzled/writemasked support here with the + * whole-variable stuff in opt_copy_propagation.cpp just made a mess, + * so this is separate despite the ACP handling being somewhat + * similar. + * + * This should reduce the number of MOV instructions in the generated + * programs unless copy propagation is also done on the LIR, and may + * help anyway by triggering other optimizations that live in the HIR. + */ + +#include "ir.h" +#include "ir_rvalue_visitor.h" +#include "ir_basic_block.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +static bool debug = false; + +class acp_entry : public exec_node +{ +public: + acp_entry(ir_variable *lhs, ir_variable *rhs, int write_mask, int swizzle[4]) + { + this->lhs = lhs; + this->rhs = rhs; + this->write_mask = write_mask; + memcpy(this->swizzle, swizzle, sizeof(this->swizzle)); + } + + acp_entry(acp_entry *a) + { + this->lhs = a->lhs; + this->rhs = a->rhs; + this->write_mask = a->write_mask; + memcpy(this->swizzle, a->swizzle, sizeof(this->swizzle)); + } + + ir_variable *lhs; + ir_variable *rhs; + unsigned int write_mask; + int swizzle[4]; +}; + + +class kill_entry : public exec_node +{ +public: + kill_entry(ir_variable *var, int write_mask) + { + this->var = var; + this->write_mask = write_mask; + } + + ir_variable *var; + unsigned int write_mask; +}; + +class ir_copy_propagation_elements_visitor : public ir_rvalue_visitor { +public: + ir_copy_propagation_elements_visitor() + { + this->progress = false; + this->mem_ctx = ralloc_context(NULL); + this->shader_mem_ctx = NULL; + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + } + ~ir_copy_propagation_elements_visitor() + { + ralloc_free(mem_ctx); + } + + virtual ir_visitor_status visit_enter(class ir_loop *); + virtual ir_visitor_status visit_enter(class ir_function_signature *); + virtual ir_visitor_status visit_leave(class ir_assignment *); + virtual ir_visitor_status visit_enter(class ir_call *); + virtual ir_visitor_status visit_enter(class ir_if *); + + void handle_rvalue(ir_rvalue **rvalue); + + void add_copy(ir_assignment *ir); + void kill(kill_entry *k); + void handle_if_block(exec_list *instructions); + + /** List of acp_entry: The available copies to propagate */ + exec_list *acp; + /** + * List of kill_entry: The variables whose values were killed in this + * block. + */ + exec_list *kills; + + bool progress; + + bool killed_all; + + /* Context for our local data structures. */ + void *mem_ctx; + /* Context for allocating new shader nodes. */ + void *shader_mem_ctx; +}; + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_function_signature *ir) +{ + /* Treat entry into a function signature as a completely separate + * block. Any instructions at global scope will be shuffled into + * main() at link time, so they're irrelevant to us. + */ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body); + + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = orig_killed_all; + + return visit_continue_with_parent; +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_leave(ir_assignment *ir) +{ + ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); + ir_variable *var = ir->lhs->variable_referenced(); + + if (var->type->is_scalar() || var->type->is_vector()) { + kill_entry *k; + + if (lhs) + k = new(mem_ctx) kill_entry(var, ir->write_mask); + else + k = new(mem_ctx) kill_entry(var, ~0); + + kill(k); + } + + add_copy(ir); + + return visit_continue; +} + +/** + * Replaces dereferences of ACP RHS variables with ACP LHS variables. + * + * This is where the actual copy propagation occurs. Note that the + * rewriting of ir_dereference means that the ir_dereference instance + * must not be shared by multiple IR operations! + */ +void +ir_copy_propagation_elements_visitor::handle_rvalue(ir_rvalue **ir) +{ + int swizzle_chan[4]; + ir_dereference_variable *deref_var; + ir_variable *source[4] = {NULL, NULL, NULL, NULL}; + int source_chan[4]; + int chans; + + if (!*ir) + return; + + ir_swizzle *swizzle = (*ir)->as_swizzle(); + if (swizzle) { + deref_var = swizzle->val->as_dereference_variable(); + if (!deref_var) + return; + + swizzle_chan[0] = swizzle->mask.x; + swizzle_chan[1] = swizzle->mask.y; + swizzle_chan[2] = swizzle->mask.z; + swizzle_chan[3] = swizzle->mask.w; + chans = swizzle->type->vector_elements; + } else { + deref_var = (*ir)->as_dereference_variable(); + if (!deref_var) + return; + + swizzle_chan[0] = 0; + swizzle_chan[1] = 1; + swizzle_chan[2] = 2; + swizzle_chan[3] = 3; + chans = deref_var->type->vector_elements; + } + + if (this->in_assignee) + return; + + ir_variable *var = deref_var->var; + + /* Try to find ACP entries covering swizzle_chan[], hoping they're + * the same source variable. + */ + foreach_iter(exec_list_iterator, iter, *this->acp) { + acp_entry *entry = (acp_entry *)iter.get(); + + if (var == entry->lhs) { + for (int c = 0; c < chans; c++) { + if (entry->write_mask & (1 << swizzle_chan[c])) { + source[c] = entry->rhs; + source_chan[c] = entry->swizzle[swizzle_chan[c]]; + } + } + } + } + + /* Make sure all channels are copying from the same source variable. */ + if (!source[0]) + return; + for (int c = 1; c < chans; c++) { + if (source[c] != source[0]) + return; + } + + if (!shader_mem_ctx) + shader_mem_ctx = ralloc_parent(deref_var); + + if (debug) { + printf("Copy propagation from:\n"); + (*ir)->print(); + } + + deref_var = new(shader_mem_ctx) ir_dereference_variable(source[0]); + *ir = new(shader_mem_ctx) ir_swizzle(deref_var, + source_chan[0], + source_chan[1], + source_chan[2], + source_chan[3], + chans); + + if (debug) { + printf("to:\n"); + (*ir)->print(); + printf("\n"); + } +} + + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_call *ir) +{ + /* Do copy propagation on call parameters, but skip any out params */ + exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); + foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { + ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); + ir_instruction *ir = (ir_instruction *)iter.get(); + if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { + ir->accept(this); + } + sig_param_iter.next(); + } + + /* Since we're unlinked, we don't (necessarily) know the side effects of + * this call. So kill all copies. + */ + acp->make_empty(); + this->killed_all = true; + + return visit_continue_with_parent; +} + +void +ir_copy_propagation_elements_visitor::handle_if_block(exec_list *instructions) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + /* Populate the initial acp with a copy of the original */ + foreach_iter(exec_list_iterator, iter, *orig_acp) { + acp_entry *a = (acp_entry *)iter.get(); + this->acp->push_tail(new(this->mem_ctx) acp_entry(a)); + } + + visit_list_elements(this, instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + /* Move the new kills into the parent block's list, removing them + * from the parent's ACP list in the process. + */ + foreach_list_safe(node, new_kills) { + kill_entry *k = (kill_entry *)node; + kill(k); + } +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_if *ir) +{ + ir->condition->accept(this); + + handle_if_block(&ir->then_instructions); + handle_if_block(&ir->else_instructions); + + /* handle_if_block() already descended into the children. */ + return visit_continue_with_parent; +} + +ir_visitor_status +ir_copy_propagation_elements_visitor::visit_enter(ir_loop *ir) +{ + exec_list *orig_acp = this->acp; + exec_list *orig_kills = this->kills; + bool orig_killed_all = this->killed_all; + + /* FINISHME: For now, the initial acp for loops is totally empty. + * We could go through once, then go through again with the acp + * cloned minus the killed entries after the first run through. + */ + this->acp = new(mem_ctx) exec_list; + this->kills = new(mem_ctx) exec_list; + this->killed_all = false; + + visit_list_elements(this, &ir->body_instructions); + + if (this->killed_all) { + orig_acp->make_empty(); + } + + exec_list *new_kills = this->kills; + this->kills = orig_kills; + this->acp = orig_acp; + this->killed_all = this->killed_all || orig_killed_all; + + foreach_list_safe(node, new_kills) { + kill_entry *k = (kill_entry *)node; + kill(k); + } + + /* already descended into the children. */ + return visit_continue_with_parent; +} + +/* Remove any entries currently in the ACP for this kill. */ +void +ir_copy_propagation_elements_visitor::kill(kill_entry *k) +{ + foreach_list_safe(node, acp) { + acp_entry *entry = (acp_entry *)node; + + if (entry->lhs == k->var) { + entry->write_mask = entry->write_mask & ~k->write_mask; + if (entry->write_mask == 0) { + entry->remove(); + continue; + } + } + if (entry->rhs == k->var) { + entry->remove(); + } + } + + /* If we were on a list, remove ourselves before inserting */ + if (k->next) + k->remove(); + + this->kills->push_tail(k); +} + +/** + * Adds directly-copied channels between vector variables to the available + * copy propagation list. + */ +void +ir_copy_propagation_elements_visitor::add_copy(ir_assignment *ir) +{ + acp_entry *entry; + int orig_swizzle[4] = {0, 1, 2, 3}; + int swizzle[4]; + + if (ir->condition) + return; + + ir_dereference_variable *lhs = ir->lhs->as_dereference_variable(); + if (!lhs || !(lhs->type->is_scalar() || lhs->type->is_vector())) + return; + + ir_dereference_variable *rhs = ir->rhs->as_dereference_variable(); + if (!rhs) { + ir_swizzle *swiz = ir->rhs->as_swizzle(); + if (!swiz) + return; + + rhs = swiz->val->as_dereference_variable(); + if (!rhs) + return; + + orig_swizzle[0] = swiz->mask.x; + orig_swizzle[1] = swiz->mask.y; + orig_swizzle[2] = swiz->mask.z; + orig_swizzle[3] = swiz->mask.w; + } + + /* Move the swizzle channels out to the positions they match in the + * destination. We don't want to have to rewrite the swizzle[] + * array every time we clear a bit of the write_mask. + */ + int j = 0; + for (int i = 0; i < 4; i++) { + if (ir->write_mask & (1 << i)) + swizzle[i] = orig_swizzle[j++]; + } + + entry = new(this->mem_ctx) acp_entry(lhs->var, rhs->var, ir->write_mask, + swizzle); + this->acp->push_tail(entry); +} + +bool +do_copy_propagation_elements(exec_list *instructions) +{ + ir_copy_propagation_elements_visitor v; + + visit_list_elements(&v, instructions); + + return v.progress; +} diff --git a/mesalib/src/glsl/opt_dead_code.cpp b/mesalib/src/glsl/opt_dead_code.cpp index 492ba73a1..cb500d2d1 100644 --- a/mesalib/src/glsl/opt_dead_code.cpp +++ b/mesalib/src/glsl/opt_dead_code.cpp @@ -1,142 +1,142 @@ -/* - * 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. - */ - -/** - * \file opt_dead_code.cpp - * - * Eliminates dead assignments and variable declarations from the code. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_variable_refcount.h" -#include "glsl_types.h" - -static bool debug = false; - -/** - * Do a dead code pass over instructions and everything that instructions - * references. - * - * Note that this will remove assignments to globals, so it is not suitable - * for usage on an unlinked instruction stream. - */ -bool -do_dead_code(exec_list *instructions) -{ - ir_variable_refcount_visitor v; - bool progress = false; - - v.run(instructions); - - foreach_iter(exec_list_iterator, iter, v.variable_list) { - variable_entry *entry = (variable_entry *)iter.get(); - - /* Since each assignment is a reference, the refereneced count must be - * greater than or equal to the assignment count. If they are equal, - * then all of the references are assignments, and the variable is - * dead. - * - * Note that if the variable is neither assigned nor referenced, both - * counts will be zero and will be caught by the equality test. - */ - assert(entry->referenced_count >= entry->assigned_count); - - if (debug) { - printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n", - entry->var->name, (void *) entry->var, - entry->referenced_count, entry->assigned_count, - entry->declaration ? "" : "not "); - } - - if ((entry->referenced_count > entry->assigned_count) - || !entry->declaration) - continue; - - if (entry->assign) { - /* Remove a single dead assignment to the variable we found. - * Don't do so if it's a shader output, though. - */ - if (entry->var->mode != ir_var_out && - entry->var->mode != ir_var_inout && - !ir_has_call(entry->assign)) { - entry->assign->remove(); - progress = true; - - if (debug) { - printf("Removed assignment to %s@%p\n", - entry->var->name, (void *) entry->var); - } - } - } else { - /* If there are no assignments or references to the variable left, - * then we can remove its declaration. - */ - - /* uniform initializers are precious, and could get used by another - * stage. - */ - if (entry->var->mode == ir_var_uniform && - entry->var->constant_value) - continue; - - entry->var->remove(); - progress = true; - - if (debug) { - printf("Removed declaration of %s@%p\n", - entry->var->name, (void *) entry->var); - } - } - } - - return progress; -} - -/** - * Does a dead code pass on the functions present in the instruction stream. - * - * This is suitable for use while the program is not linked, as it will - * ignore variable declarations (and the assignments to them) for variables - * with global scope. - */ -bool -do_dead_code_unlinked(exec_list *instructions) -{ - bool progress = false; - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_function *f = ir->as_function(); - if (f) { - foreach_iter(exec_list_iterator, sigiter, *f) { - ir_function_signature *sig = - (ir_function_signature *) sigiter.get(); - if (do_dead_code(&sig->body)) - progress = true; - } - } - } - - return progress; -} +/* + * 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. + */ + +/** + * \file opt_dead_code.cpp + * + * Eliminates dead assignments and variable declarations from the code. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_variable_refcount.h" +#include "glsl_types.h" + +static bool debug = false; + +/** + * Do a dead code pass over instructions and everything that instructions + * references. + * + * Note that this will remove assignments to globals, so it is not suitable + * for usage on an unlinked instruction stream. + */ +bool +do_dead_code(exec_list *instructions) +{ + ir_variable_refcount_visitor v; + bool progress = false; + + v.run(instructions); + + foreach_iter(exec_list_iterator, iter, v.variable_list) { + variable_entry *entry = (variable_entry *)iter.get(); + + /* Since each assignment is a reference, the refereneced count must be + * greater than or equal to the assignment count. If they are equal, + * then all of the references are assignments, and the variable is + * dead. + * + * Note that if the variable is neither assigned nor referenced, both + * counts will be zero and will be caught by the equality test. + */ + assert(entry->referenced_count >= entry->assigned_count); + + if (debug) { + printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n", + entry->var->name, (void *) entry->var, + entry->referenced_count, entry->assigned_count, + entry->declaration ? "" : "not "); + } + + if ((entry->referenced_count > entry->assigned_count) + || !entry->declaration) + continue; + + if (entry->assign) { + /* Remove a single dead assignment to the variable we found. + * Don't do so if it's a shader output, though. + */ + if (entry->var->mode != ir_var_out && + entry->var->mode != ir_var_inout && + !ir_has_call(entry->assign)) { + entry->assign->remove(); + progress = true; + + if (debug) { + printf("Removed assignment to %s@%p\n", + entry->var->name, (void *) entry->var); + } + } + } else { + /* If there are no assignments or references to the variable left, + * then we can remove its declaration. + */ + + /* uniform initializers are precious, and could get used by another + * stage. + */ + if (entry->var->mode == ir_var_uniform && + entry->var->constant_value) + continue; + + entry->var->remove(); + progress = true; + + if (debug) { + printf("Removed declaration of %s@%p\n", + entry->var->name, (void *) entry->var); + } + } + } + + return progress; +} + +/** + * Does a dead code pass on the functions present in the instruction stream. + * + * This is suitable for use while the program is not linked, as it will + * ignore variable declarations (and the assignments to them) for variables + * with global scope. + */ +bool +do_dead_code_unlinked(exec_list *instructions) +{ + bool progress = false; + + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_function *f = ir->as_function(); + if (f) { + foreach_iter(exec_list_iterator, sigiter, *f) { + ir_function_signature *sig = + (ir_function_signature *) sigiter.get(); + if (do_dead_code(&sig->body)) + progress = true; + } + } + } + + return progress; +} diff --git a/mesalib/src/glsl/opt_dead_code_local.cpp b/mesalib/src/glsl/opt_dead_code_local.cpp index 42e5e94c8..39962bd60 100644 --- a/mesalib/src/glsl/opt_dead_code_local.cpp +++ b/mesalib/src/glsl/opt_dead_code_local.cpp @@ -1,222 +1,222 @@ -/* - * 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. - */ - -/** - * \file opt_dead_code_local.cpp - * - * Eliminates local dead assignments from the code. - * - * This operates on basic blocks, tracking assignments and finding if - * they're used before the variable is completely reassigned. - * - * Compare this to ir_dead_code.cpp, which operates globally looking - * for assignments to variables that are never read. - */ - -#include "ir.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -static bool debug = false; - -class assignment_entry : public exec_node -{ -public: - assignment_entry(ir_variable *lhs, ir_instruction *ir) - { - assert(lhs); - assert(ir); - this->lhs = lhs; - this->ir = ir; - } - - ir_variable *lhs; - ir_instruction *ir; -}; - -class kill_for_derefs_visitor : public ir_hierarchical_visitor { -public: - kill_for_derefs_visitor(exec_list *assignments) - { - this->assignments = assignments; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - ir_variable *const var = ir->variable_referenced(); - - foreach_iter(exec_list_iterator, iter, *this->assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - if (entry->lhs == var) { - if (debug) - printf("kill %s\n", entry->lhs->name); - entry->remove(); - } - } - - return visit_continue; - } - -private: - exec_list *assignments; -}; - -class array_index_visit : public ir_hierarchical_visitor { -public: - array_index_visit(ir_hierarchical_visitor *v) - { - this->visitor = v; - } - - virtual ir_visitor_status visit_enter(class ir_dereference_array *ir) - { - ir->array_index->accept(visitor); - return visit_continue; - } - - static void run(ir_instruction *ir, ir_hierarchical_visitor *v) - { - array_index_visit top_visit(v); - ir->accept(& top_visit); - } - - ir_hierarchical_visitor *visitor; -}; - - -/** - * Adds an entry to the available copy list if it's a plain assignment - * of a variable to a variable. - */ -static bool -process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments) -{ - ir_variable *var = NULL; - bool progress = false; - kill_for_derefs_visitor v(assignments); - - /* Kill assignment entries for things used to produce this assignment. */ - ir->rhs->accept(&v); - if (ir->condition) { - ir->condition->accept(&v); - } - - /* Kill assignment enties used as array indices. - */ - array_index_visit::run(ir->lhs, &v); - var = ir->lhs->variable_referenced(); - assert(var); - - /* Now, check if we did a whole-variable assignment. */ - if (!ir->condition && (ir->whole_variable_written() != NULL)) { - /* We did a whole-variable assignment. So, any instruction in - * the assignment list with the same LHS is dead. - */ - if (debug) - printf("looking for %s to remove\n", var->name); - foreach_iter(exec_list_iterator, iter, *assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - if (entry->lhs == var) { - if (debug) - printf("removing %s\n", var->name); - entry->ir->remove(); - entry->remove(); - progress = true; - } - } - } - - /* Add this instruction to the assignment list available to be removed. - * But not if the assignment has other side effects. - */ - if (ir_has_call(ir)) - return progress; - - assignment_entry *entry = new(ctx) assignment_entry(var, ir); - assignments->push_tail(entry); - - if (debug) { - printf("add %s\n", var->name); - - printf("current entries\n"); - foreach_iter(exec_list_iterator, iter, *assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - printf(" %s\n", entry->lhs->name); - } - } - - return progress; -} - -static void -dead_code_local_basic_block(ir_instruction *first, - ir_instruction *last, - void *data) -{ - ir_instruction *ir, *ir_next; - /* List of avaialble_copy */ - exec_list assignments; - bool *out_progress = (bool *)data; - bool progress = false; - - void *ctx = ralloc_context(NULL); - /* Safe looping, since process_assignment */ - for (ir = first, ir_next = (ir_instruction *)first->next;; - ir = ir_next, ir_next = (ir_instruction *)ir->next) { - ir_assignment *ir_assign = ir->as_assignment(); - - if (debug) { - ir->print(); - printf("\n"); - } - - if (ir_assign) { - progress = process_assignment(ctx, ir_assign, &assignments) || progress; - } else { - kill_for_derefs_visitor kill(&assignments); - ir->accept(&kill); - } - - if (ir == last) - break; - } - *out_progress = progress; - ralloc_free(ctx); -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_dead_code_local(exec_list *instructions) -{ - bool progress = false; - - call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress); - - return progress; -} +/* + * 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. + */ + +/** + * \file opt_dead_code_local.cpp + * + * Eliminates local dead assignments from the code. + * + * This operates on basic blocks, tracking assignments and finding if + * they're used before the variable is completely reassigned. + * + * Compare this to ir_dead_code.cpp, which operates globally looking + * for assignments to variables that are never read. + */ + +#include "ir.h" +#include "ir_basic_block.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +static bool debug = false; + +class assignment_entry : public exec_node +{ +public: + assignment_entry(ir_variable *lhs, ir_instruction *ir) + { + assert(lhs); + assert(ir); + this->lhs = lhs; + this->ir = ir; + } + + ir_variable *lhs; + ir_instruction *ir; +}; + +class kill_for_derefs_visitor : public ir_hierarchical_visitor { +public: + kill_for_derefs_visitor(exec_list *assignments) + { + this->assignments = assignments; + } + + virtual ir_visitor_status visit(ir_dereference_variable *ir) + { + ir_variable *const var = ir->variable_referenced(); + + foreach_iter(exec_list_iterator, iter, *this->assignments) { + assignment_entry *entry = (assignment_entry *)iter.get(); + + if (entry->lhs == var) { + if (debug) + printf("kill %s\n", entry->lhs->name); + entry->remove(); + } + } + + return visit_continue; + } + +private: + exec_list *assignments; +}; + +class array_index_visit : public ir_hierarchical_visitor { +public: + array_index_visit(ir_hierarchical_visitor *v) + { + this->visitor = v; + } + + virtual ir_visitor_status visit_enter(class ir_dereference_array *ir) + { + ir->array_index->accept(visitor); + return visit_continue; + } + + static void run(ir_instruction *ir, ir_hierarchical_visitor *v) + { + array_index_visit top_visit(v); + ir->accept(& top_visit); + } + + ir_hierarchical_visitor *visitor; +}; + + +/** + * Adds an entry to the available copy list if it's a plain assignment + * of a variable to a variable. + */ +static bool +process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments) +{ + ir_variable *var = NULL; + bool progress = false; + kill_for_derefs_visitor v(assignments); + + /* Kill assignment entries for things used to produce this assignment. */ + ir->rhs->accept(&v); + if (ir->condition) { + ir->condition->accept(&v); + } + + /* Kill assignment enties used as array indices. + */ + array_index_visit::run(ir->lhs, &v); + var = ir->lhs->variable_referenced(); + assert(var); + + /* Now, check if we did a whole-variable assignment. */ + if (!ir->condition && (ir->whole_variable_written() != NULL)) { + /* We did a whole-variable assignment. So, any instruction in + * the assignment list with the same LHS is dead. + */ + if (debug) + printf("looking for %s to remove\n", var->name); + foreach_iter(exec_list_iterator, iter, *assignments) { + assignment_entry *entry = (assignment_entry *)iter.get(); + + if (entry->lhs == var) { + if (debug) + printf("removing %s\n", var->name); + entry->ir->remove(); + entry->remove(); + progress = true; + } + } + } + + /* Add this instruction to the assignment list available to be removed. + * But not if the assignment has other side effects. + */ + if (ir_has_call(ir)) + return progress; + + assignment_entry *entry = new(ctx) assignment_entry(var, ir); + assignments->push_tail(entry); + + if (debug) { + printf("add %s\n", var->name); + + printf("current entries\n"); + foreach_iter(exec_list_iterator, iter, *assignments) { + assignment_entry *entry = (assignment_entry *)iter.get(); + + printf(" %s\n", entry->lhs->name); + } + } + + return progress; +} + +static void +dead_code_local_basic_block(ir_instruction *first, + ir_instruction *last, + void *data) +{ + ir_instruction *ir, *ir_next; + /* List of avaialble_copy */ + exec_list assignments; + bool *out_progress = (bool *)data; + bool progress = false; + + void *ctx = ralloc_context(NULL); + /* Safe looping, since process_assignment */ + for (ir = first, ir_next = (ir_instruction *)first->next;; + ir = ir_next, ir_next = (ir_instruction *)ir->next) { + ir_assignment *ir_assign = ir->as_assignment(); + + if (debug) { + ir->print(); + printf("\n"); + } + + if (ir_assign) { + progress = process_assignment(ctx, ir_assign, &assignments) || progress; + } else { + kill_for_derefs_visitor kill(&assignments); + ir->accept(&kill); + } + + if (ir == last) + break; + } + *out_progress = progress; + ralloc_free(ctx); +} + +/** + * Does a copy propagation pass on the code present in the instruction stream. + */ +bool +do_dead_code_local(exec_list *instructions) +{ + bool progress = false; + + call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress); + + return progress; +} diff --git a/mesalib/src/glsl/opt_function_inlining.cpp b/mesalib/src/glsl/opt_function_inlining.cpp index 3cc405b95..8fef358cc 100644 --- a/mesalib/src/glsl/opt_function_inlining.cpp +++ b/mesalib/src/glsl/opt_function_inlining.cpp @@ -1,422 +1,422 @@ -/* - * 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. - */ - -/** - * \file opt_function_inlining.cpp - * - * Replaces calls to functions with the body of the function. - */ - -#include -#include "ir.h" -#include "ir_visitor.h" -#include "ir_function_inlining.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" -#include "program/hash_table.h" - -static void -do_sampler_replacement(exec_list *instructions, - ir_variable *sampler, - ir_dereference *deref); - -class ir_function_inlining_visitor : public ir_hierarchical_visitor { -public: - ir_function_inlining_visitor() - { - progress = false; - } - - virtual ~ir_function_inlining_visitor() - { - /* empty */ - } - - virtual ir_visitor_status visit_enter(ir_expression *); - virtual ir_visitor_status visit_enter(ir_call *); - virtual ir_visitor_status visit_enter(ir_assignment *); - virtual ir_visitor_status visit_enter(ir_return *); - virtual ir_visitor_status visit_enter(ir_texture *); - virtual ir_visitor_status visit_enter(ir_swizzle *); - - bool progress; -}; - - -bool -automatic_inlining_predicate(ir_instruction *ir) -{ - ir_call *call = ir->as_call(); - - if (call && can_inline(call)) - return true; - - return false; -} - -bool -do_function_inlining(exec_list *instructions) -{ - ir_function_inlining_visitor v; - - do_expression_flattening(instructions, automatic_inlining_predicate); - - v.run(instructions); - - return v.progress; -} - -static void -replace_return_with_assignment(ir_instruction *ir, void *data) -{ - void *ctx = ralloc_parent(ir); - ir_variable *retval = (ir_variable *)data; - ir_return *ret = ir->as_return(); - - if (ret) { - if (ret->value) { - ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval); - ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL)); - } else { - /* un-valued return has to be the last return, or we shouldn't - * have reached here. (see can_inline()). - */ - assert(ret->next->is_tail_sentinel()); - ret->remove(); - } - } -} - -ir_rvalue * -ir_call::generate_inline(ir_instruction *next_ir) -{ - void *ctx = ralloc_parent(this); - ir_variable **parameters; - int num_parameters; - int i; - ir_variable *retval = NULL; - struct hash_table *ht; - - ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); - - num_parameters = 0; - foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters) - num_parameters++; - - parameters = new ir_variable *[num_parameters]; - - /* Generate storage for the return value. */ - if (!this->callee->return_type->is_void()) { - retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val", - ir_var_auto); - next_ir->insert_before(retval); - } - - /* Generate the declarations for the parameters to our inlined code, - * and set up the mapping of real function body variables to ours. - */ - i = 0; - exec_list_iterator sig_param_iter = this->callee->parameters.iterator(); - exec_list_iterator param_iter = this->actual_parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); - ir_rvalue *param = (ir_rvalue *) param_iter.get(); - - /* Generate a new variable for the parameter. */ - if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { - /* For samplers, we want the inlined sampler references - * referencing the passed in sampler variable, since that - * will have the location information, which an assignment of - * a sampler wouldn't. Fix it up below. - */ - parameters[i] = NULL; - } else { - parameters[i] = sig_param->clone(ctx, ht); - parameters[i]->mode = ir_var_auto; - - /* Remove the read-only decoration becuase we're going to write - * directly to this variable. If the cloned variable is left - * read-only and the inlined function is inside a loop, the loop - * analysis code will get confused. - */ - parameters[i]->read_only = false; - next_ir->insert_before(parameters[i]); - } - - /* Move the actual param into our param variable if it's an 'in' type. */ - if (parameters[i] && (sig_param->mode == ir_var_in || - sig_param->mode == ir_var_const_in || - sig_param->mode == ir_var_inout)) { - ir_assignment *assign; - - assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]), - param, NULL); - next_ir->insert_before(assign); - } - - sig_param_iter.next(); - param_iter.next(); - } - - exec_list new_instructions; - - /* Generate the inlined body of the function to a new list */ - foreach_iter(exec_list_iterator, iter, callee->body) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_instruction *new_ir = ir->clone(ctx, ht); - - new_instructions.push_tail(new_ir); - visit_tree(new_ir, replace_return_with_assignment, retval); - } - - /* If any samplers were passed in, replace any deref of the sampler - * with a deref of the sampler argument. - */ - param_iter = this->actual_parameters.iterator(); - sig_param_iter = this->callee->parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_instruction *const param = (ir_instruction *) param_iter.get(); - ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); - - if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { - ir_dereference *deref = param->as_dereference(); - - assert(deref); - do_sampler_replacement(&new_instructions, sig_param, deref); - } - param_iter.next(); - sig_param_iter.next(); - } - - /* Now push those new instructions in. */ - next_ir->insert_before(&new_instructions); - - /* Copy back the value of any 'out' parameters from the function body - * variables to our own. - */ - i = 0; - param_iter = this->actual_parameters.iterator(); - sig_param_iter = this->callee->parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_instruction *const param = (ir_instruction *) param_iter.get(); - const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get(); - - /* Move our param variable into the actual param if it's an 'out' type. */ - if (parameters[i] && (sig_param->mode == ir_var_out || - sig_param->mode == ir_var_inout)) { - ir_assignment *assign; - - assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(), - new(ctx) ir_dereference_variable(parameters[i]), - NULL); - next_ir->insert_before(assign); - } - - param_iter.next(); - sig_param_iter.next(); - } - - delete [] parameters; - - hash_table_dtor(ht); - - if (retval) - return new(ctx) ir_dereference_variable(retval); - else - return NULL; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_expression *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_return *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_texture *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_swizzle *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_call *ir) -{ - if (can_inline(ir)) { - /* If the call was part of some tree, then it should have been - * flattened out or we shouldn't have seen it because of a - * visit_continue_with_parent in this visitor. - */ - assert(ir == base_ir); - - (void) ir->generate_inline(ir); - ir->remove(); - this->progress = true; - } - - return visit_continue; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_assignment *ir) -{ - ir_call *call = ir->rhs->as_call(); - if (!call || !can_inline(call)) - return visit_continue; - - /* generates the parameter setup, function body, and returns the return - * value of the function - */ - ir_rvalue *rhs = call->generate_inline(ir); - assert(rhs); - - ir->rhs = rhs; - this->progress = true; - - return visit_continue; -} - -/** - * Replaces references to the "sampler" variable with a clone of "deref." - * - * From the spec, samplers can appear in the tree as function - * (non-out) parameters and as the result of array indexing and - * structure field selection. In our builtin implementation, they - * also appear in the sampler field of an ir_tex instruction. - */ - -class ir_sampler_replacement_visitor : public ir_hierarchical_visitor { -public: - ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref) - { - this->sampler = sampler; - this->deref = deref; - } - - virtual ~ir_sampler_replacement_visitor() - { - } - - virtual ir_visitor_status visit_leave(ir_call *); - virtual ir_visitor_status visit_leave(ir_dereference_array *); - virtual ir_visitor_status visit_leave(ir_dereference_record *); - virtual ir_visitor_status visit_leave(ir_texture *); - - void replace_deref(ir_dereference **deref); - void replace_rvalue(ir_rvalue **rvalue); - - ir_variable *sampler; - ir_dereference *deref; -}; - -void -ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref) -{ - ir_dereference_variable *deref_var = (*deref)->as_dereference_variable(); - if (deref_var && deref_var->var == this->sampler) { - *deref = this->deref->clone(ralloc_parent(*deref), NULL); - } -} - -void -ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue) -{ - if (!*rvalue) - return; - - ir_dereference *deref = (*rvalue)->as_dereference(); - - if (!deref) - return; - - replace_deref(&deref); - *rvalue = deref; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_texture *ir) -{ - replace_deref(&ir->sampler); - - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir) -{ - replace_rvalue(&ir->array); - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir) -{ - replace_rvalue(&ir->record); - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_call *ir) -{ - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param = (ir_rvalue *)iter.get(); - ir_rvalue *new_param = param; - replace_rvalue(&new_param); - - if (new_param != param) { - param->replace_with(new_param); - } - } - return visit_continue; -} - -static void -do_sampler_replacement(exec_list *instructions, - ir_variable *sampler, - ir_dereference *deref) -{ - ir_sampler_replacement_visitor v(sampler, deref); - - visit_list_elements(&v, instructions); -} +/* + * 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. + */ + +/** + * \file opt_function_inlining.cpp + * + * Replaces calls to functions with the body of the function. + */ + +#include +#include "ir.h" +#include "ir_visitor.h" +#include "ir_function_inlining.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" +#include "program/hash_table.h" + +static void +do_sampler_replacement(exec_list *instructions, + ir_variable *sampler, + ir_dereference *deref); + +class ir_function_inlining_visitor : public ir_hierarchical_visitor { +public: + ir_function_inlining_visitor() + { + progress = false; + } + + virtual ~ir_function_inlining_visitor() + { + /* empty */ + } + + virtual ir_visitor_status visit_enter(ir_expression *); + virtual ir_visitor_status visit_enter(ir_call *); + virtual ir_visitor_status visit_enter(ir_assignment *); + virtual ir_visitor_status visit_enter(ir_return *); + virtual ir_visitor_status visit_enter(ir_texture *); + virtual ir_visitor_status visit_enter(ir_swizzle *); + + bool progress; +}; + + +bool +automatic_inlining_predicate(ir_instruction *ir) +{ + ir_call *call = ir->as_call(); + + if (call && can_inline(call)) + return true; + + return false; +} + +bool +do_function_inlining(exec_list *instructions) +{ + ir_function_inlining_visitor v; + + do_expression_flattening(instructions, automatic_inlining_predicate); + + v.run(instructions); + + return v.progress; +} + +static void +replace_return_with_assignment(ir_instruction *ir, void *data) +{ + void *ctx = ralloc_parent(ir); + ir_variable *retval = (ir_variable *)data; + ir_return *ret = ir->as_return(); + + if (ret) { + if (ret->value) { + ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval); + ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL)); + } else { + /* un-valued return has to be the last return, or we shouldn't + * have reached here. (see can_inline()). + */ + assert(ret->next->is_tail_sentinel()); + ret->remove(); + } + } +} + +ir_rvalue * +ir_call::generate_inline(ir_instruction *next_ir) +{ + void *ctx = ralloc_parent(this); + ir_variable **parameters; + int num_parameters; + int i; + ir_variable *retval = NULL; + struct hash_table *ht; + + ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); + + num_parameters = 0; + foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters) + num_parameters++; + + parameters = new ir_variable *[num_parameters]; + + /* Generate storage for the return value. */ + if (!this->callee->return_type->is_void()) { + retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val", + ir_var_auto); + next_ir->insert_before(retval); + } + + /* Generate the declarations for the parameters to our inlined code, + * and set up the mapping of real function body variables to ours. + */ + i = 0; + exec_list_iterator sig_param_iter = this->callee->parameters.iterator(); + exec_list_iterator param_iter = this->actual_parameters.iterator(); + for (i = 0; i < num_parameters; i++) { + ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); + ir_rvalue *param = (ir_rvalue *) param_iter.get(); + + /* Generate a new variable for the parameter. */ + if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { + /* For samplers, we want the inlined sampler references + * referencing the passed in sampler variable, since that + * will have the location information, which an assignment of + * a sampler wouldn't. Fix it up below. + */ + parameters[i] = NULL; + } else { + parameters[i] = sig_param->clone(ctx, ht); + parameters[i]->mode = ir_var_auto; + + /* Remove the read-only decoration becuase we're going to write + * directly to this variable. If the cloned variable is left + * read-only and the inlined function is inside a loop, the loop + * analysis code will get confused. + */ + parameters[i]->read_only = false; + next_ir->insert_before(parameters[i]); + } + + /* Move the actual param into our param variable if it's an 'in' type. */ + if (parameters[i] && (sig_param->mode == ir_var_in || + sig_param->mode == ir_var_const_in || + sig_param->mode == ir_var_inout)) { + ir_assignment *assign; + + assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]), + param, NULL); + next_ir->insert_before(assign); + } + + sig_param_iter.next(); + param_iter.next(); + } + + exec_list new_instructions; + + /* Generate the inlined body of the function to a new list */ + foreach_iter(exec_list_iterator, iter, callee->body) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_instruction *new_ir = ir->clone(ctx, ht); + + new_instructions.push_tail(new_ir); + visit_tree(new_ir, replace_return_with_assignment, retval); + } + + /* If any samplers were passed in, replace any deref of the sampler + * with a deref of the sampler argument. + */ + param_iter = this->actual_parameters.iterator(); + sig_param_iter = this->callee->parameters.iterator(); + for (i = 0; i < num_parameters; i++) { + ir_instruction *const param = (ir_instruction *) param_iter.get(); + ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); + + if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { + ir_dereference *deref = param->as_dereference(); + + assert(deref); + do_sampler_replacement(&new_instructions, sig_param, deref); + } + param_iter.next(); + sig_param_iter.next(); + } + + /* Now push those new instructions in. */ + next_ir->insert_before(&new_instructions); + + /* Copy back the value of any 'out' parameters from the function body + * variables to our own. + */ + i = 0; + param_iter = this->actual_parameters.iterator(); + sig_param_iter = this->callee->parameters.iterator(); + for (i = 0; i < num_parameters; i++) { + ir_instruction *const param = (ir_instruction *) param_iter.get(); + const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get(); + + /* Move our param variable into the actual param if it's an 'out' type. */ + if (parameters[i] && (sig_param->mode == ir_var_out || + sig_param->mode == ir_var_inout)) { + ir_assignment *assign; + + assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(), + new(ctx) ir_dereference_variable(parameters[i]), + NULL); + next_ir->insert_before(assign); + } + + param_iter.next(); + sig_param_iter.next(); + } + + delete [] parameters; + + hash_table_dtor(ht); + + if (retval) + return new(ctx) ir_dereference_variable(retval); + else + return NULL; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_expression *ir) +{ + (void) ir; + return visit_continue_with_parent; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_return *ir) +{ + (void) ir; + return visit_continue_with_parent; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_texture *ir) +{ + (void) ir; + return visit_continue_with_parent; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_swizzle *ir) +{ + (void) ir; + return visit_continue_with_parent; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_call *ir) +{ + if (can_inline(ir)) { + /* If the call was part of some tree, then it should have been + * flattened out or we shouldn't have seen it because of a + * visit_continue_with_parent in this visitor. + */ + assert(ir == base_ir); + + (void) ir->generate_inline(ir); + ir->remove(); + this->progress = true; + } + + return visit_continue; +} + + +ir_visitor_status +ir_function_inlining_visitor::visit_enter(ir_assignment *ir) +{ + ir_call *call = ir->rhs->as_call(); + if (!call || !can_inline(call)) + return visit_continue; + + /* generates the parameter setup, function body, and returns the return + * value of the function + */ + ir_rvalue *rhs = call->generate_inline(ir); + assert(rhs); + + ir->rhs = rhs; + this->progress = true; + + return visit_continue; +} + +/** + * Replaces references to the "sampler" variable with a clone of "deref." + * + * From the spec, samplers can appear in the tree as function + * (non-out) parameters and as the result of array indexing and + * structure field selection. In our builtin implementation, they + * also appear in the sampler field of an ir_tex instruction. + */ + +class ir_sampler_replacement_visitor : public ir_hierarchical_visitor { +public: + ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref) + { + this->sampler = sampler; + this->deref = deref; + } + + virtual ~ir_sampler_replacement_visitor() + { + } + + virtual ir_visitor_status visit_leave(ir_call *); + virtual ir_visitor_status visit_leave(ir_dereference_array *); + virtual ir_visitor_status visit_leave(ir_dereference_record *); + virtual ir_visitor_status visit_leave(ir_texture *); + + void replace_deref(ir_dereference **deref); + void replace_rvalue(ir_rvalue **rvalue); + + ir_variable *sampler; + ir_dereference *deref; +}; + +void +ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref) +{ + ir_dereference_variable *deref_var = (*deref)->as_dereference_variable(); + if (deref_var && deref_var->var == this->sampler) { + *deref = this->deref->clone(ralloc_parent(*deref), NULL); + } +} + +void +ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue) +{ + if (!*rvalue) + return; + + ir_dereference *deref = (*rvalue)->as_dereference(); + + if (!deref) + return; + + replace_deref(&deref); + *rvalue = deref; +} + +ir_visitor_status +ir_sampler_replacement_visitor::visit_leave(ir_texture *ir) +{ + replace_deref(&ir->sampler); + + return visit_continue; +} + +ir_visitor_status +ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir) +{ + replace_rvalue(&ir->array); + return visit_continue; +} + +ir_visitor_status +ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir) +{ + replace_rvalue(&ir->record); + return visit_continue; +} + +ir_visitor_status +ir_sampler_replacement_visitor::visit_leave(ir_call *ir) +{ + foreach_iter(exec_list_iterator, iter, *ir) { + ir_rvalue *param = (ir_rvalue *)iter.get(); + ir_rvalue *new_param = param; + replace_rvalue(&new_param); + + if (new_param != param) { + param->replace_with(new_param); + } + } + return visit_continue; +} + +static void +do_sampler_replacement(exec_list *instructions, + ir_variable *sampler, + ir_dereference *deref) +{ + ir_sampler_replacement_visitor v(sampler, deref); + + visit_list_elements(&v, instructions); +} diff --git a/mesalib/src/glsl/opt_swizzle_swizzle.cpp b/mesalib/src/glsl/opt_swizzle_swizzle.cpp index e23655240..bc442fa86 100644 --- a/mesalib/src/glsl/opt_swizzle_swizzle.cpp +++ b/mesalib/src/glsl/opt_swizzle_swizzle.cpp @@ -1,93 +1,93 @@ -/* - * 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. - */ - -/** - * \file opt_swizzle_swizzle.cpp - * - * Eliminates the second swizzle in a swizzle chain. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor { -public: - ir_swizzle_swizzle_visitor() - { - progress = false; - } - - virtual ir_visitor_status visit_enter(ir_swizzle *); - - bool progress; -}; - -ir_visitor_status -ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir) -{ - int mask2[4]; - - ir_swizzle *swiz2 = ir->val->as_swizzle(); - if (!swiz2) - return visit_continue; - - memset(&mask2, 0, sizeof(mask2)); - if (swiz2->mask.num_components >= 1) - mask2[0] = swiz2->mask.x; - if (swiz2->mask.num_components >= 2) - mask2[1] = swiz2->mask.y; - if (swiz2->mask.num_components >= 3) - mask2[2] = swiz2->mask.z; - if (swiz2->mask.num_components >= 4) - mask2[3] = swiz2->mask.w; - - if (ir->mask.num_components >= 1) - ir->mask.x = mask2[ir->mask.x]; - if (ir->mask.num_components >= 2) - ir->mask.y = mask2[ir->mask.y]; - if (ir->mask.num_components >= 3) - ir->mask.z = mask2[ir->mask.z]; - if (ir->mask.num_components >= 4) - ir->mask.w = mask2[ir->mask.w]; - - ir->val = swiz2->val; - - this->progress = true; - - return visit_continue; -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_swizzle_swizzle(exec_list *instructions) -{ - ir_swizzle_swizzle_visitor v; - - v.run(instructions); - - return v.progress; -} +/* + * 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. + */ + +/** + * \file opt_swizzle_swizzle.cpp + * + * Eliminates the second swizzle in a swizzle chain. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor { +public: + ir_swizzle_swizzle_visitor() + { + progress = false; + } + + virtual ir_visitor_status visit_enter(ir_swizzle *); + + bool progress; +}; + +ir_visitor_status +ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir) +{ + int mask2[4]; + + ir_swizzle *swiz2 = ir->val->as_swizzle(); + if (!swiz2) + return visit_continue; + + memset(&mask2, 0, sizeof(mask2)); + if (swiz2->mask.num_components >= 1) + mask2[0] = swiz2->mask.x; + if (swiz2->mask.num_components >= 2) + mask2[1] = swiz2->mask.y; + if (swiz2->mask.num_components >= 3) + mask2[2] = swiz2->mask.z; + if (swiz2->mask.num_components >= 4) + mask2[3] = swiz2->mask.w; + + if (ir->mask.num_components >= 1) + ir->mask.x = mask2[ir->mask.x]; + if (ir->mask.num_components >= 2) + ir->mask.y = mask2[ir->mask.y]; + if (ir->mask.num_components >= 3) + ir->mask.z = mask2[ir->mask.z]; + if (ir->mask.num_components >= 4) + ir->mask.w = mask2[ir->mask.w]; + + ir->val = swiz2->val; + + this->progress = true; + + return visit_continue; +} + +/** + * Does a copy propagation pass on the code present in the instruction stream. + */ +bool +do_swizzle_swizzle(exec_list *instructions) +{ + ir_swizzle_swizzle_visitor v; + + v.run(instructions); + + return v.progress; +} diff --git a/mesalib/src/glsl/opt_tree_grafting.cpp b/mesalib/src/glsl/opt_tree_grafting.cpp index e08545b47..22a1749b9 100644 --- a/mesalib/src/glsl/opt_tree_grafting.cpp +++ b/mesalib/src/glsl/opt_tree_grafting.cpp @@ -1,369 +1,369 @@ -/* - * 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. - */ - -/** - * \file opt_tree_grafting.cpp - * - * Takes assignments to variables that are dereferenced only once and - * pastes the RHS expression into where the variable is dereferenced. - * - * In the process of various operations like function inlining and - * tertiary op handling, we'll end up with our expression trees having - * been chopped up into a series of assignments of short expressions - * to temps. Other passes like ir_algebraic.cpp would prefer to see - * the deepest expression trees they can to try to optimize them. - * - * This is a lot like copy propagaton. In comparison, copy - * propagation only acts on plain copies, not arbitrary expressions on - * the RHS. Generally, we wouldn't want to go pasting some - * complicated expression everywhere it got used, though, so we don't - * handle expressions in that pass. - * - * The hard part is making sure we don't move an expression across - * some other assignments that would change the value of the - * expression. So we split this into two passes: First, find the - * variables in our scope which are written to once and read once, and - * then go through basic blocks seeing if we find an opportunity to - * move those expressions safely. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_variable_refcount.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -static bool debug = false; - -class ir_tree_grafting_visitor : public ir_hierarchical_visitor { -public: - ir_tree_grafting_visitor(ir_assignment *graft_assign, - ir_variable *graft_var) - { - this->progress = false; - this->graft_assign = graft_assign; - this->graft_var = graft_var; - } - - virtual ir_visitor_status visit_leave(class ir_assignment *); - virtual ir_visitor_status visit_enter(class ir_call *); - virtual ir_visitor_status visit_enter(class ir_expression *); - virtual ir_visitor_status visit_enter(class ir_function *); - virtual ir_visitor_status visit_enter(class ir_function_signature *); - virtual ir_visitor_status visit_enter(class ir_if *); - virtual ir_visitor_status visit_enter(class ir_loop *); - virtual ir_visitor_status visit_enter(class ir_swizzle *); - virtual ir_visitor_status visit_enter(class ir_texture *); - - bool do_graft(ir_rvalue **rvalue); - - bool progress; - ir_variable *graft_var; - ir_assignment *graft_assign; -}; - -struct find_deref_info { - ir_variable *var; - bool found; -}; - -void -dereferences_variable_callback(ir_instruction *ir, void *data) -{ - struct find_deref_info *info = (struct find_deref_info *)data; - ir_dereference_variable *deref = ir->as_dereference_variable(); - - if (deref && deref->var == info->var) - info->found = true; -} - -static bool -dereferences_variable(ir_instruction *ir, ir_variable *var) -{ - struct find_deref_info info; - - info.var = var; - info.found = false; - - visit_tree(ir, dereferences_variable_callback, &info); - - return info.found; -} - -bool -ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue) -{ - if (!*rvalue) - return false; - - ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); - - if (!deref || deref->var != this->graft_var) - return false; - - if (debug) { - printf("GRAFTING:\n"); - this->graft_assign->print(); - printf("\n"); - printf("TO:\n"); - (*rvalue)->print(); - printf("\n"); - } - - this->graft_assign->remove(); - *rvalue = this->graft_assign->rhs; - - this->progress = true; - return true; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_loop *ir) -{ - (void)ir; - /* Do not traverse into the body of the loop since that is a - * different basic block. - */ - return visit_stop; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_leave(ir_assignment *ir) -{ - if (do_graft(&ir->rhs) || - do_graft(&ir->condition)) - return visit_stop; - - /* If this assignment updates a variable used in the assignment - * we're trying to graft, then we're done. - */ - if (dereferences_variable(this->graft_assign->rhs, - ir->lhs->variable_referenced())) { - if (debug) { - printf("graft killed by: "); - ir->print(); - printf("\n"); - } - return visit_stop; - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_function *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir) -{ - (void)ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_call *ir) -{ - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - /* Reminder: iterating ir_call iterates its parameters. */ - foreach_iter(exec_list_iterator, iter, *ir) { - ir_variable *sig_param = (ir_variable *)sig_iter.get(); - ir_rvalue *ir = (ir_rvalue *)iter.get(); - ir_rvalue *new_ir = ir; - - if (sig_param->mode != ir_var_in && sig_param->mode != ir_var_const_in) - continue; - - if (do_graft(&new_ir)) { - ir->replace_with(new_ir); - return visit_stop; - } - sig_iter.next(); - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_expression *ir) -{ - for (unsigned int i = 0; i < ir->get_num_operands(); i++) { - if (do_graft(&ir->operands[i])) - return visit_stop; - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_if *ir) -{ - if (do_graft(&ir->condition)) - return visit_stop; - - /* Do not traverse into the body of the if-statement since that is a - * different basic block. - */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir) -{ - if (do_graft(&ir->val)) - return visit_stop; - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_texture *ir) -{ - if (do_graft(&ir->coordinate) || - do_graft(&ir->projector) || - do_graft(&ir->offset) || - do_graft(&ir->shadow_comparitor)) - return visit_stop; - - switch (ir->op) { - case ir_tex: - break; - case ir_txb: - if (do_graft(&ir->lod_info.bias)) - return visit_stop; - break; - case ir_txf: - case ir_txl: - case ir_txs: - if (do_graft(&ir->lod_info.lod)) - return visit_stop; - break; - case ir_txd: - if (do_graft(&ir->lod_info.grad.dPdx) || - do_graft(&ir->lod_info.grad.dPdy)) - return visit_stop; - break; - } - - return visit_continue; -} - -struct tree_grafting_info { - ir_variable_refcount_visitor *refs; - bool progress; -}; - -static bool -try_tree_grafting(ir_assignment *start, - ir_variable *lhs_var, - ir_instruction *bb_last) -{ - ir_tree_grafting_visitor v(start, lhs_var); - - if (debug) { - printf("trying to graft: "); - lhs_var->print(); - printf("\n"); - } - - for (ir_instruction *ir = (ir_instruction *)start->next; - ir != bb_last->next; - ir = (ir_instruction *)ir->next) { - - if (debug) { - printf("- "); - ir->print(); - printf("\n"); - } - - ir_visitor_status s = ir->accept(&v); - if (s == visit_stop) - return v.progress; - } - - return false; -} - -static void -tree_grafting_basic_block(ir_instruction *bb_first, - ir_instruction *bb_last, - void *data) -{ - struct tree_grafting_info *info = (struct tree_grafting_info *)data; - ir_instruction *ir, *next; - - for (ir = bb_first, next = (ir_instruction *)ir->next; - ir != bb_last->next; - ir = next, next = (ir_instruction *)ir->next) { - ir_assignment *assign = ir->as_assignment(); - - if (!assign) - continue; - - ir_variable *lhs_var = assign->whole_variable_written(); - if (!lhs_var) - continue; - - if (lhs_var->mode == ir_var_out || - lhs_var->mode == ir_var_inout) - continue; - - variable_entry *entry = info->refs->get_variable_entry(lhs_var); - - if (!entry->declaration || - entry->assigned_count != 1 || - entry->referenced_count != 2) - continue; - - assert(assign == entry->assign); - - /* Found a possibly graftable assignment. Now, walk through the - * rest of the BB seeing if the deref is here, and if nothing interfered with - * pasting its expression's values in between. - */ - info->progress |= try_tree_grafting(assign, lhs_var, bb_last); - } -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_tree_grafting(exec_list *instructions) -{ - ir_variable_refcount_visitor refs; - struct tree_grafting_info info; - - info.progress = false; - info.refs = &refs; - - visit_list_elements(info.refs, instructions); - - call_for_basic_blocks(instructions, tree_grafting_basic_block, &info); - - return info.progress; -} +/* + * 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. + */ + +/** + * \file opt_tree_grafting.cpp + * + * Takes assignments to variables that are dereferenced only once and + * pastes the RHS expression into where the variable is dereferenced. + * + * In the process of various operations like function inlining and + * tertiary op handling, we'll end up with our expression trees having + * been chopped up into a series of assignments of short expressions + * to temps. Other passes like ir_algebraic.cpp would prefer to see + * the deepest expression trees they can to try to optimize them. + * + * This is a lot like copy propagaton. In comparison, copy + * propagation only acts on plain copies, not arbitrary expressions on + * the RHS. Generally, we wouldn't want to go pasting some + * complicated expression everywhere it got used, though, so we don't + * handle expressions in that pass. + * + * The hard part is making sure we don't move an expression across + * some other assignments that would change the value of the + * expression. So we split this into two passes: First, find the + * variables in our scope which are written to once and read once, and + * then go through basic blocks seeing if we find an opportunity to + * move those expressions safely. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_variable_refcount.h" +#include "ir_basic_block.h" +#include "ir_optimization.h" +#include "glsl_types.h" + +static bool debug = false; + +class ir_tree_grafting_visitor : public ir_hierarchical_visitor { +public: + ir_tree_grafting_visitor(ir_assignment *graft_assign, + ir_variable *graft_var) + { + this->progress = false; + this->graft_assign = graft_assign; + this->graft_var = graft_var; + } + + virtual ir_visitor_status visit_leave(class ir_assignment *); + virtual ir_visitor_status visit_enter(class ir_call *); + virtual ir_visitor_status visit_enter(class ir_expression *); + virtual ir_visitor_status visit_enter(class ir_function *); + virtual ir_visitor_status visit_enter(class ir_function_signature *); + virtual ir_visitor_status visit_enter(class ir_if *); + virtual ir_visitor_status visit_enter(class ir_loop *); + virtual ir_visitor_status visit_enter(class ir_swizzle *); + virtual ir_visitor_status visit_enter(class ir_texture *); + + bool do_graft(ir_rvalue **rvalue); + + bool progress; + ir_variable *graft_var; + ir_assignment *graft_assign; +}; + +struct find_deref_info { + ir_variable *var; + bool found; +}; + +void +dereferences_variable_callback(ir_instruction *ir, void *data) +{ + struct find_deref_info *info = (struct find_deref_info *)data; + ir_dereference_variable *deref = ir->as_dereference_variable(); + + if (deref && deref->var == info->var) + info->found = true; +} + +static bool +dereferences_variable(ir_instruction *ir, ir_variable *var) +{ + struct find_deref_info info; + + info.var = var; + info.found = false; + + visit_tree(ir, dereferences_variable_callback, &info); + + return info.found; +} + +bool +ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue) +{ + if (!*rvalue) + return false; + + ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); + + if (!deref || deref->var != this->graft_var) + return false; + + if (debug) { + printf("GRAFTING:\n"); + this->graft_assign->print(); + printf("\n"); + printf("TO:\n"); + (*rvalue)->print(); + printf("\n"); + } + + this->graft_assign->remove(); + *rvalue = this->graft_assign->rhs; + + this->progress = true; + return true; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_loop *ir) +{ + (void)ir; + /* Do not traverse into the body of the loop since that is a + * different basic block. + */ + return visit_stop; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_leave(ir_assignment *ir) +{ + if (do_graft(&ir->rhs) || + do_graft(&ir->condition)) + return visit_stop; + + /* If this assignment updates a variable used in the assignment + * we're trying to graft, then we're done. + */ + if (dereferences_variable(this->graft_assign->rhs, + ir->lhs->variable_referenced())) { + if (debug) { + printf("graft killed by: "); + ir->print(); + printf("\n"); + } + return visit_stop; + } + + return visit_continue; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_function *ir) +{ + (void) ir; + return visit_continue_with_parent; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir) +{ + (void)ir; + return visit_continue_with_parent; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_call *ir) +{ + exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); + /* Reminder: iterating ir_call iterates its parameters. */ + foreach_iter(exec_list_iterator, iter, *ir) { + ir_variable *sig_param = (ir_variable *)sig_iter.get(); + ir_rvalue *ir = (ir_rvalue *)iter.get(); + ir_rvalue *new_ir = ir; + + if (sig_param->mode != ir_var_in && sig_param->mode != ir_var_const_in) + continue; + + if (do_graft(&new_ir)) { + ir->replace_with(new_ir); + return visit_stop; + } + sig_iter.next(); + } + + return visit_continue; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_expression *ir) +{ + for (unsigned int i = 0; i < ir->get_num_operands(); i++) { + if (do_graft(&ir->operands[i])) + return visit_stop; + } + + return visit_continue; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_if *ir) +{ + if (do_graft(&ir->condition)) + return visit_stop; + + /* Do not traverse into the body of the if-statement since that is a + * different basic block. + */ + return visit_continue_with_parent; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir) +{ + if (do_graft(&ir->val)) + return visit_stop; + + return visit_continue; +} + +ir_visitor_status +ir_tree_grafting_visitor::visit_enter(ir_texture *ir) +{ + if (do_graft(&ir->coordinate) || + do_graft(&ir->projector) || + do_graft(&ir->offset) || + do_graft(&ir->shadow_comparitor)) + return visit_stop; + + switch (ir->op) { + case ir_tex: + break; + case ir_txb: + if (do_graft(&ir->lod_info.bias)) + return visit_stop; + break; + case ir_txf: + case ir_txl: + case ir_txs: + if (do_graft(&ir->lod_info.lod)) + return visit_stop; + break; + case ir_txd: + if (do_graft(&ir->lod_info.grad.dPdx) || + do_graft(&ir->lod_info.grad.dPdy)) + return visit_stop; + break; + } + + return visit_continue; +} + +struct tree_grafting_info { + ir_variable_refcount_visitor *refs; + bool progress; +}; + +static bool +try_tree_grafting(ir_assignment *start, + ir_variable *lhs_var, + ir_instruction *bb_last) +{ + ir_tree_grafting_visitor v(start, lhs_var); + + if (debug) { + printf("trying to graft: "); + lhs_var->print(); + printf("\n"); + } + + for (ir_instruction *ir = (ir_instruction *)start->next; + ir != bb_last->next; + ir = (ir_instruction *)ir->next) { + + if (debug) { + printf("- "); + ir->print(); + printf("\n"); + } + + ir_visitor_status s = ir->accept(&v); + if (s == visit_stop) + return v.progress; + } + + return false; +} + +static void +tree_grafting_basic_block(ir_instruction *bb_first, + ir_instruction *bb_last, + void *data) +{ + struct tree_grafting_info *info = (struct tree_grafting_info *)data; + ir_instruction *ir, *next; + + for (ir = bb_first, next = (ir_instruction *)ir->next; + ir != bb_last->next; + ir = next, next = (ir_instruction *)ir->next) { + ir_assignment *assign = ir->as_assignment(); + + if (!assign) + continue; + + ir_variable *lhs_var = assign->whole_variable_written(); + if (!lhs_var) + continue; + + if (lhs_var->mode == ir_var_out || + lhs_var->mode == ir_var_inout) + continue; + + variable_entry *entry = info->refs->get_variable_entry(lhs_var); + + if (!entry->declaration || + entry->assigned_count != 1 || + entry->referenced_count != 2) + continue; + + assert(assign == entry->assign); + + /* Found a possibly graftable assignment. Now, walk through the + * rest of the BB seeing if the deref is here, and if nothing interfered with + * pasting its expression's values in between. + */ + info->progress |= try_tree_grafting(assign, lhs_var, bb_last); + } +} + +/** + * Does a copy propagation pass on the code present in the instruction stream. + */ +bool +do_tree_grafting(exec_list *instructions) +{ + ir_variable_refcount_visitor refs; + struct tree_grafting_info info; + + info.progress = false; + info.refs = &refs; + + visit_list_elements(info.refs, instructions); + + call_for_basic_blocks(instructions, tree_grafting_basic_block, &info); + + return info.progress; +} diff --git a/mesalib/src/glsl/s_expression.cpp b/mesalib/src/glsl/s_expression.cpp index b6a51c34e..e704a3be2 100644 --- a/mesalib/src/glsl/s_expression.cpp +++ b/mesalib/src/glsl/s_expression.cpp @@ -1,211 +1,211 @@ -/* -*- c++ -*- */ -/* - * 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 -#include "s_expression.h" - -s_symbol::s_symbol(const char *str, size_t n) -{ - /* Assume the given string is already nul-terminated and in memory that - * will live as long as this node. - */ - assert(str[n] == '\0'); - this->str = str; -} - -s_list::s_list() -{ -} - -static void -skip_whitespace(const char *&src, char *&symbol_buffer) -{ - size_t n = strspn(src, " \v\t\r\n"); - src += n; - symbol_buffer += n; - /* Also skip Scheme-style comments: semi-colon 'til end of line */ - if (src[0] == ';') { - n = strcspn(src, "\n"); - src += n; - symbol_buffer += n; - skip_whitespace(src, symbol_buffer); - } -} - -static s_expression * -read_atom(void *ctx, const char *&src, char *&symbol_buffer) -{ - s_expression *expr = NULL; - - skip_whitespace(src, symbol_buffer); - - size_t n = strcspn(src, "( \v\t\r\n);"); - if (n == 0) - return NULL; // no atom - - // Check if the atom is a number. - char *float_end = NULL; - double f = glsl_strtod(src, &float_end); - if (float_end != src) { - char *int_end = NULL; - int i = strtol(src, &int_end, 10); - // If strtod matched more characters, it must have a decimal part - if (float_end > int_end) - expr = new(ctx) s_float(f); - else - expr = new(ctx) s_int(i); - } else { - // Not a number; return a symbol. - symbol_buffer[n] = '\0'; - expr = new(ctx) s_symbol(symbol_buffer, n); - } - - src += n; - symbol_buffer += n; - - return expr; -} - -static s_expression * -__read_expression(void *ctx, const char *&src, char *&symbol_buffer) -{ - s_expression *atom = read_atom(ctx, src, symbol_buffer); - if (atom != NULL) - return atom; - - skip_whitespace(src, symbol_buffer); - if (src[0] == '(') { - ++src; - ++symbol_buffer; - - s_list *list = new(ctx) s_list; - s_expression *expr; - - while ((expr = __read_expression(ctx, src, symbol_buffer)) != NULL) { - list->subexpressions.push_tail(expr); - } - skip_whitespace(src, symbol_buffer); - if (src[0] != ')') { - printf("Unclosed expression (check your parenthesis).\n"); - return NULL; - } - ++src; - ++symbol_buffer; - return list; - } - return NULL; -} - -s_expression * -s_expression::read_expression(void *ctx, const char *&src) -{ - assert(src != NULL); - - /* When we encounter a Symbol, we need to save a nul-terminated copy of - * the string. However, ralloc_strndup'ing every individual Symbol is - * extremely expensive. We could avoid this by simply overwriting the - * next character (guaranteed to be whitespace, parens, or semicolon) with - * a nul-byte. But overwriting non-whitespace would mess up parsing. - * - * So, just copy the whole buffer ahead of time. Walk both, leaving the - * original source string unmodified, and altering the copy to contain the - * necessary nul-bytes whenever we encounter a symbol. - */ - char *symbol_buffer = ralloc_strdup(ctx, src); - return __read_expression(ctx, src, symbol_buffer); -} - -void s_int::print() -{ - printf("%d", this->val); -} - -void s_float::print() -{ - printf("%f", this->val); -} - -void s_symbol::print() -{ - printf("%s", this->str); -} - -void s_list::print() -{ - printf("("); - foreach_iter(exec_list_iterator, it, this->subexpressions) { - s_expression *expr = (s_expression*) it.get(); - expr->print(); - if (!expr->next->is_tail_sentinel()) - printf(" "); - } - printf(")"); -} - -// -------------------------------------------------- - -bool -s_pattern::match(s_expression *expr) -{ - switch (type) - { - case EXPR: *p_expr = expr; break; - case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break; - case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break; - case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break; - case INT: if (expr->is_int()) *p_int = (s_int *) expr; break; - case STRING: - s_symbol *sym = SX_AS_SYMBOL(expr); - if (sym != NULL && strcmp(sym->value(), literal) == 0) - return true; - return false; - }; - - return *p_expr == expr; -} - -bool -s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial) -{ - s_list *list = SX_AS_LIST(top); - if (list == NULL) - return false; - - unsigned i = 0; - foreach_iter(exec_list_iterator, it, list->subexpressions) { - if (i >= n) - return partial; /* More actual items than the pattern expected */ - - s_expression *expr = (s_expression *) it.get(); - if (expr == NULL || !pattern[i].match(expr)) - return false; - - i++; - } - - if (i < n) - return false; /* Less actual items than the pattern expected */ - - return true; -} +/* -*- c++ -*- */ +/* + * 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 +#include "s_expression.h" + +s_symbol::s_symbol(const char *str, size_t n) +{ + /* Assume the given string is already nul-terminated and in memory that + * will live as long as this node. + */ + assert(str[n] == '\0'); + this->str = str; +} + +s_list::s_list() +{ +} + +static void +skip_whitespace(const char *&src, char *&symbol_buffer) +{ + size_t n = strspn(src, " \v\t\r\n"); + src += n; + symbol_buffer += n; + /* Also skip Scheme-style comments: semi-colon 'til end of line */ + if (src[0] == ';') { + n = strcspn(src, "\n"); + src += n; + symbol_buffer += n; + skip_whitespace(src, symbol_buffer); + } +} + +static s_expression * +read_atom(void *ctx, const char *&src, char *&symbol_buffer) +{ + s_expression *expr = NULL; + + skip_whitespace(src, symbol_buffer); + + size_t n = strcspn(src, "( \v\t\r\n);"); + if (n == 0) + return NULL; // no atom + + // Check if the atom is a number. + char *float_end = NULL; + double f = glsl_strtod(src, &float_end); + if (float_end != src) { + char *int_end = NULL; + int i = strtol(src, &int_end, 10); + // If strtod matched more characters, it must have a decimal part + if (float_end > int_end) + expr = new(ctx) s_float(f); + else + expr = new(ctx) s_int(i); + } else { + // Not a number; return a symbol. + symbol_buffer[n] = '\0'; + expr = new(ctx) s_symbol(symbol_buffer, n); + } + + src += n; + symbol_buffer += n; + + return expr; +} + +static s_expression * +__read_expression(void *ctx, const char *&src, char *&symbol_buffer) +{ + s_expression *atom = read_atom(ctx, src, symbol_buffer); + if (atom != NULL) + return atom; + + skip_whitespace(src, symbol_buffer); + if (src[0] == '(') { + ++src; + ++symbol_buffer; + + s_list *list = new(ctx) s_list; + s_expression *expr; + + while ((expr = __read_expression(ctx, src, symbol_buffer)) != NULL) { + list->subexpressions.push_tail(expr); + } + skip_whitespace(src, symbol_buffer); + if (src[0] != ')') { + printf("Unclosed expression (check your parenthesis).\n"); + return NULL; + } + ++src; + ++symbol_buffer; + return list; + } + return NULL; +} + +s_expression * +s_expression::read_expression(void *ctx, const char *&src) +{ + assert(src != NULL); + + /* When we encounter a Symbol, we need to save a nul-terminated copy of + * the string. However, ralloc_strndup'ing every individual Symbol is + * extremely expensive. We could avoid this by simply overwriting the + * next character (guaranteed to be whitespace, parens, or semicolon) with + * a nul-byte. But overwriting non-whitespace would mess up parsing. + * + * So, just copy the whole buffer ahead of time. Walk both, leaving the + * original source string unmodified, and altering the copy to contain the + * necessary nul-bytes whenever we encounter a symbol. + */ + char *symbol_buffer = ralloc_strdup(ctx, src); + return __read_expression(ctx, src, symbol_buffer); +} + +void s_int::print() +{ + printf("%d", this->val); +} + +void s_float::print() +{ + printf("%f", this->val); +} + +void s_symbol::print() +{ + printf("%s", this->str); +} + +void s_list::print() +{ + printf("("); + foreach_iter(exec_list_iterator, it, this->subexpressions) { + s_expression *expr = (s_expression*) it.get(); + expr->print(); + if (!expr->next->is_tail_sentinel()) + printf(" "); + } + printf(")"); +} + +// -------------------------------------------------- + +bool +s_pattern::match(s_expression *expr) +{ + switch (type) + { + case EXPR: *p_expr = expr; break; + case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break; + case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break; + case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break; + case INT: if (expr->is_int()) *p_int = (s_int *) expr; break; + case STRING: + s_symbol *sym = SX_AS_SYMBOL(expr); + if (sym != NULL && strcmp(sym->value(), literal) == 0) + return true; + return false; + }; + + return *p_expr == expr; +} + +bool +s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial) +{ + s_list *list = SX_AS_LIST(top); + if (list == NULL) + return false; + + unsigned i = 0; + foreach_iter(exec_list_iterator, it, list->subexpressions) { + if (i >= n) + return partial; /* More actual items than the pattern expected */ + + s_expression *expr = (s_expression *) it.get(); + if (expr == NULL || !pattern[i].match(expr)) + return false; + + i++; + } + + if (i < n) + return false; /* Less actual items than the pattern expected */ + + return true; +} -- cgit v1.2.3