From 5e633abcca598289d0423d89bb400b41e6417259 Mon Sep 17 00:00:00 2001 From: marha Date: Tue, 15 Mar 2011 21:35:41 +0000 Subject: xserver libX11 libxcb mesa git update 15 Mar 2011 --- mesalib/src/glsl/SConscript | 9 +- mesalib/src/glsl/ir.cpp | 3148 +++++++++++----------- mesalib/src/glsl/ir.h | 3280 +++++++++++------------ mesalib/src/glsl/ir_print_visitor.cpp | 945 +++---- mesalib/src/glsl/ir_reader.cpp | 2001 +++++++------- mesalib/src/glsl/opt_dead_functions.cpp | 317 +-- mesalib/src/glsl/opt_discard_simplification.cpp | 10 + mesalib/src/glsl/opt_if_simplification.cpp | 10 + mesalib/src/glsl/opt_redundant_jumps.cpp | 231 +- mesalib/src/glsl/opt_structure_splitting.cpp | 728 ++--- 10 files changed, 5369 insertions(+), 5310 deletions(-) (limited to 'mesalib/src/glsl') diff --git a/mesalib/src/glsl/SConscript b/mesalib/src/glsl/SConscript index d7c3459f2..5d97756e1 100644 --- a/mesalib/src/glsl/SConscript +++ b/mesalib/src/glsl/SConscript @@ -105,11 +105,16 @@ if env['msvc']: if env['crosscompile'] and env['platform'] != 'embedded': Import('builtin_glsl_function') else: + # Copy these files to avoid generation object files into src/mesa/program + env.Prepend(CPPPATH = ['#src/mesa/program']) + env.Command('hash_table.c', '#src/mesa/program/hash_table.c', Copy('$TARGET', '$SOURCE')) + env.Command('symbol_table.c', '#src/mesa/program/symbol_table.c', Copy('$TARGET', '$SOURCE')) + main_obj = env.StaticObject('main.cpp') mesa_objs = env.StaticObject([ - '#src/mesa/program/hash_table.c', - '#src/mesa/program/symbol_table.c', + 'hash_table.c', + 'symbol_table.c', ]) builtin_compiler = env.Program( diff --git a/mesalib/src/glsl/ir.cpp b/mesalib/src/glsl/ir.cpp index fc356ba52..ceb989110 100644 --- a/mesalib/src/glsl/ir.cpp +++ b/mesalib/src/glsl/ir.cpp @@ -1,1576 +1,1572 @@ -/* - * 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: - 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_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", - "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 type_contains_sampler(const glsl_type *type) -{ - if (type->is_array()) { - return type_contains_sampler(type->fields.array); - } else if (type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - if (type_contains_sampler(type->fields.structure[i].type)) - return true; - } - return false; - } else { - return type->is_sampler(); - } -} - -bool -ir_dereference::is_lvalue() -{ - 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 (type_contains_sampler(this->type)) - return false; - - return true; -} - - -const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf" }; - -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) -{ - assert(sampler != NULL); - this->sampler = sampler; - - switch (sampler->type->sampler_type) { - case GLSL_TYPE_FLOAT: - this->type = glsl_type::vec4_type; - break; - case GLSL_TYPE_INT: - this->type = glsl_type::ivec4_type; - break; - case GLSL_TYPE_UINT: - this->type = glsl_type::uvec4_type; - break; - } -} - - -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() -{ - 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: + 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_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", + "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 type_contains_sampler(const glsl_type *type) +{ + if (type->is_array()) { + return type_contains_sampler(type->fields.array); + } else if (type->is_record()) { + for (unsigned int i = 0; i < type->length; i++) { + if (type_contains_sampler(type->fields.structure[i].type)) + return true; + } + return false; + } else { + return type->is_sampler(); + } +} + +bool +ir_dereference::is_lvalue() +{ + 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 (type_contains_sampler(this->type)) + return false; + + return true; +} + + +const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf" }; + +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; + + assert(sampler->type->sampler_type == 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() +{ + 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 f2f902c0a..72e3e35c4 100644 --- a/mesalib/src/glsl/ir.h +++ b/mesalib/src/glsl/ir.h @@ -1,1640 +1,1640 @@ -/* -*- 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() - { - return false; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() - { - 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_conservative_depth extension allows 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); - -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; - - /** - * 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); - - /** - * 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_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); - } - - 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; - -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 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 ) - */ -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 infer the type. */ - void set_sampler(ir_dereference *sampler); - - /** - * 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() - { - return val->is_lvalue() && !mask.has_duplicates; - } - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced(); - - 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(); - - /** - * Get the variable that is ultimately referenced by an r-value - */ - virtual ir_variable *variable_referenced() = 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() - { - 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() - { - 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() - { - 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); - -/** - * 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); - -#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() + { + return false; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() + { + 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_conservative_depth extension allows 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); + +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; + + /** + * 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); + + /** + * 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_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); + } + + 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; + +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 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 ) + */ +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() + { + return val->is_lvalue() && !mask.has_duplicates; + } + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced(); + + 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(); + + /** + * Get the variable that is ultimately referenced by an r-value + */ + virtual ir_variable *variable_referenced() = 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() + { + 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() + { + 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() + { + 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); + +/** + * 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); + +#endif /* IR_H */ diff --git a/mesalib/src/glsl/ir_print_visitor.cpp b/mesalib/src/glsl/ir_print_visitor.cpp index 82ccc722f..3c5c4fd20 100644 --- a/mesalib/src/glsl/ir_print_visitor.cpp +++ b/mesalib/src/glsl/ir_print_visitor.cpp @@ -1,471 +1,474 @@ -/* - * 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" - -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)"); -} - - -void ir_print_visitor::indent(void) -{ - for (int i = 0; i < indentation; i++) - printf(" "); -} - -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@%p)", ir->name, (void *) ir); -} - - -void ir_print_visitor::visit(ir_function_signature *ir) -{ - 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--; -} - - -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()); - - ir->sampler->accept(this); - printf(" "); - - ir->coordinate->accept(this); - - printf(" "); - - if (ir->offset != NULL) { - ir->offset->accept(this); - } else { - printf("0"); - } - - printf(" "); - - if (ir->op != ir_txf) { - 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: - 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@%p) ", var->name, (void *) 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->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" + +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)"); +} + + +void ir_print_visitor::indent(void) +{ + for (int i = 0; i < indentation; i++) + printf(" "); +} + +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@%p)", ir->name, (void *) ir); +} + + +void ir_print_visitor::visit(ir_function_signature *ir) +{ + 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--; +} + + +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(" "); + + ir->coordinate->accept(this); + + printf(" "); + + if (ir->offset != NULL) { + ir->offset->accept(this); + } else { + printf("0"); + } + + printf(" "); + + if (ir->op != ir_txf) { + 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: + 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@%p) ", var->name, (void *) 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->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_reader.cpp b/mesalib/src/glsl/ir_reader.cpp index af85e06ae..201e436be 100644 --- a/mesalib/src/glsl/ir_reader.cpp +++ b/mesalib/src/glsl/ir_reader.cpp @@ -1,996 +1,1005 @@ -/* - * 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_reader.h" -#include "glsl_parser_extras.h" -#include "glsl_types.h" -#include "s_expression.h" - -const static bool debug = false; - -class ir_reader { -public: - ir_reader(_mesa_glsl_parse_state *); - - void read(exec_list *instructions, const char *src, bool scan_for_protos); - -private: - void *mem_ctx; - _mesa_glsl_parse_state *state; - - void ir_read_error(s_expression *, const char *fmt, ...); - - const glsl_type *read_type(s_expression *); - - void scan_for_prototypes(exec_list *, s_expression *); - ir_function *read_function(s_expression *, bool skip_body); - void read_function_sig(ir_function *, s_expression *, bool skip_body); - - void read_instructions(exec_list *, s_expression *, ir_loop *); - ir_instruction *read_instruction(s_expression *, ir_loop *); - ir_variable *read_declaration(s_expression *); - ir_if *read_if(s_expression *, ir_loop *); - ir_loop *read_loop(s_expression *); - ir_return *read_return(s_expression *); - ir_rvalue *read_rvalue(s_expression *); - ir_assignment *read_assignment(s_expression *); - ir_expression *read_expression(s_expression *); - ir_call *read_call(s_expression *); - ir_swizzle *read_swizzle(s_expression *); - ir_constant *read_constant(s_expression *); - ir_texture *read_texture(s_expression *); - - ir_dereference *read_dereference(s_expression *); -}; - -ir_reader::ir_reader(_mesa_glsl_parse_state *state) : state(state) -{ - this->mem_ctx = state; -} - -void -_mesa_glsl_read_ir(_mesa_glsl_parse_state *state, exec_list *instructions, - const char *src, bool scan_for_protos) -{ - ir_reader r(state); - r.read(instructions, src, scan_for_protos); -} - -void -ir_reader::read(exec_list *instructions, const char *src, bool scan_for_protos) -{ - s_expression *expr = s_expression::read_expression(mem_ctx, src); - if (expr == NULL) { - ir_read_error(NULL, "couldn't parse S-Expression."); - return; - } - - if (scan_for_protos) { - scan_for_prototypes(instructions, expr); - if (state->error) - return; - } - - read_instructions(instructions, expr, NULL); - ralloc_free(expr); - - if (debug) - validate_ir_tree(instructions); -} - -void -ir_reader::ir_read_error(s_expression *expr, const char *fmt, ...) -{ - va_list ap; - - state->error = true; - - if (state->current_function != NULL) - ralloc_asprintf_append(&state->info_log, "In function %s:\n", - state->current_function->function_name()); - ralloc_strcat(&state->info_log, "error: "); - - va_start(ap, fmt); - ralloc_vasprintf_append(&state->info_log, fmt, ap); - va_end(ap); - ralloc_strcat(&state->info_log, "\n"); - - if (expr != NULL) { - ralloc_strcat(&state->info_log, "...in this context:\n "); - expr->print(); - ralloc_strcat(&state->info_log, "\n\n"); - } -} - -const glsl_type * -ir_reader::read_type(s_expression *expr) -{ - s_expression *s_base_type; - s_int *s_size; - - s_pattern pat[] = { "array", s_base_type, s_size }; - if (MATCH(expr, pat)) { - const glsl_type *base_type = read_type(s_base_type); - if (base_type == NULL) { - ir_read_error(NULL, "when reading base type of array type"); - return NULL; - } - - return glsl_type::get_array_instance(base_type, s_size->value()); - } - - s_symbol *type_sym = SX_AS_SYMBOL(expr); - if (type_sym == NULL) { - ir_read_error(expr, "expected "); - return NULL; - } - - const glsl_type *type = state->symbols->get_type(type_sym->value()); - if (type == NULL) - ir_read_error(expr, "invalid type: %s", type_sym->value()); - - return type; -} - - -void -ir_reader::scan_for_prototypes(exec_list *instructions, s_expression *expr) -{ - s_list *list = SX_AS_LIST(expr); - if (list == NULL) { - ir_read_error(expr, "Expected ( ...); found an atom."); - return; - } - - foreach_iter(exec_list_iterator, it, list->subexpressions) { - s_list *sub = SX_AS_LIST(it.get()); - if (sub == NULL) - continue; // not a (function ...); ignore it. - - s_symbol *tag = SX_AS_SYMBOL(sub->subexpressions.get_head()); - if (tag == NULL || strcmp(tag->value(), "function") != 0) - continue; // not a (function ...); ignore it. - - ir_function *f = read_function(sub, true); - if (f == NULL) - return; - instructions->push_tail(f); - } -} - -ir_function * -ir_reader::read_function(s_expression *expr, bool skip_body) -{ - bool added = false; - s_symbol *name; - - s_pattern pat[] = { "function", name }; - if (!PARTIAL_MATCH(expr, pat)) { - ir_read_error(expr, "Expected (function (signature ...) ...)"); - return NULL; - } - - ir_function *f = state->symbols->get_function(name->value()); - if (f == NULL) { - f = new(mem_ctx) ir_function(name->value()); - added = state->symbols->add_function(f); - assert(added); - } - - exec_list_iterator it = ((s_list *) expr)->subexpressions.iterator(); - it.next(); // skip "function" tag - it.next(); // skip function name - for (/* nothing */; it.has_next(); it.next()) { - s_expression *s_sig = (s_expression *) it.get(); - read_function_sig(f, s_sig, skip_body); - } - return added ? f : NULL; -} - -void -ir_reader::read_function_sig(ir_function *f, s_expression *expr, bool skip_body) -{ - s_expression *type_expr; - s_list *paramlist; - s_list *body_list; - - s_pattern pat[] = { "signature", type_expr, paramlist, body_list }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "Expected (signature (parameters ...) " - "( ...))"); - return; - } - - const glsl_type *return_type = read_type(type_expr); - if (return_type == NULL) - return; - - s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head()); - if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) { - ir_read_error(paramlist, "Expected (parameters ...)"); - return; - } - - // Read the parameters list into a temporary place. - exec_list hir_parameters; - state->symbols->push_scope(); - - exec_list_iterator it = paramlist->subexpressions.iterator(); - for (it.next() /* skip "parameters" */; it.has_next(); it.next()) { - ir_variable *var = read_declaration((s_expression *) it.get()); - if (var == NULL) - return; - - hir_parameters.push_tail(var); - } - - ir_function_signature *sig = f->exact_matching_signature(&hir_parameters); - if (sig == NULL && skip_body) { - /* If scanning for prototypes, generate a new signature. */ - sig = new(mem_ctx) ir_function_signature(return_type); - sig->is_builtin = true; - f->add_signature(sig); - } else if (sig != NULL) { - const char *badvar = sig->qualifiers_match(&hir_parameters); - if (badvar != NULL) { - ir_read_error(expr, "function `%s' parameter `%s' qualifiers " - "don't match prototype", f->name, badvar); - return; - } - - if (sig->return_type != return_type) { - ir_read_error(expr, "function `%s' return type doesn't " - "match prototype", f->name); - return; - } - } else { - /* No prototype for this body exists - skip it. */ - state->symbols->pop_scope(); - return; - } - assert(sig != NULL); - - sig->replace_parameters(&hir_parameters); - - if (!skip_body && !body_list->subexpressions.is_empty()) { - if (sig->is_defined) { - ir_read_error(expr, "function %s redefined", f->name); - return; - } - state->current_function = sig; - read_instructions(&sig->body, body_list, NULL); - state->current_function = NULL; - sig->is_defined = true; - } - - state->symbols->pop_scope(); -} - -void -ir_reader::read_instructions(exec_list *instructions, s_expression *expr, - ir_loop *loop_ctx) -{ - // Read in a list of instructions - s_list *list = SX_AS_LIST(expr); - if (list == NULL) { - ir_read_error(expr, "Expected ( ...); found an atom."); - return; - } - - foreach_iter(exec_list_iterator, it, list->subexpressions) { - s_expression *sub = (s_expression*) it.get(); - ir_instruction *ir = read_instruction(sub, loop_ctx); - if (ir != NULL) { - /* Global variable declarations should be moved to the top, before - * any functions that might use them. Functions are added to the - * instruction stream when scanning for prototypes, so without this - * hack, they always appear before variable declarations. - */ - if (state->current_function == NULL && ir->as_variable() != NULL) - instructions->push_head(ir); - else - instructions->push_tail(ir); - } - } -} - - -ir_instruction * -ir_reader::read_instruction(s_expression *expr, ir_loop *loop_ctx) -{ - s_symbol *symbol = SX_AS_SYMBOL(expr); - if (symbol != NULL) { - if (strcmp(symbol->value(), "break") == 0 && loop_ctx != NULL) - return new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_break); - if (strcmp(symbol->value(), "continue") == 0 && loop_ctx != NULL) - return new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue); - } - - s_list *list = SX_AS_LIST(expr); - if (list == NULL || list->subexpressions.is_empty()) { - ir_read_error(expr, "Invalid instruction.\n"); - return NULL; - } - - s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.get_head()); - if (tag == NULL) { - ir_read_error(expr, "expected instruction tag"); - return NULL; - } - - ir_instruction *inst = NULL; - if (strcmp(tag->value(), "declare") == 0) { - inst = read_declaration(list); - } else if (strcmp(tag->value(), "assign") == 0) { - inst = read_assignment(list); - } else if (strcmp(tag->value(), "if") == 0) { - inst = read_if(list, loop_ctx); - } else if (strcmp(tag->value(), "loop") == 0) { - inst = read_loop(list); - } else if (strcmp(tag->value(), "return") == 0) { - inst = read_return(list); - } else if (strcmp(tag->value(), "function") == 0) { - inst = read_function(list, false); - } else { - inst = read_rvalue(list); - if (inst == NULL) - ir_read_error(NULL, "when reading instruction"); - } - return inst; -} - -ir_variable * -ir_reader::read_declaration(s_expression *expr) -{ - s_list *s_quals; - s_expression *s_type; - s_symbol *s_name; - - s_pattern pat[] = { "declare", s_quals, s_type, s_name }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (declare () )"); - return NULL; - } - - const glsl_type *type = read_type(s_type); - if (type == NULL) - return NULL; - - ir_variable *var = new(mem_ctx) ir_variable(type, s_name->value(), - ir_var_auto); - - foreach_iter(exec_list_iterator, it, s_quals->subexpressions) { - s_symbol *qualifier = SX_AS_SYMBOL(it.get()); - if (qualifier == NULL) { - ir_read_error(expr, "qualifier list must contain only symbols"); - return NULL; - } - - // FINISHME: Check for duplicate/conflicting qualifiers. - if (strcmp(qualifier->value(), "centroid") == 0) { - var->centroid = 1; - } else if (strcmp(qualifier->value(), "invariant") == 0) { - var->invariant = 1; - } else if (strcmp(qualifier->value(), "uniform") == 0) { - var->mode = ir_var_uniform; - } else if (strcmp(qualifier->value(), "auto") == 0) { - var->mode = ir_var_auto; - } else if (strcmp(qualifier->value(), "in") == 0) { - var->mode = ir_var_in; - } else if (strcmp(qualifier->value(), "const_in") == 0) { - var->mode = ir_var_const_in; - } else if (strcmp(qualifier->value(), "out") == 0) { - var->mode = ir_var_out; - } else if (strcmp(qualifier->value(), "inout") == 0) { - var->mode = ir_var_inout; - } else if (strcmp(qualifier->value(), "smooth") == 0) { - var->interpolation = ir_var_smooth; - } else if (strcmp(qualifier->value(), "flat") == 0) { - var->interpolation = ir_var_flat; - } else if (strcmp(qualifier->value(), "noperspective") == 0) { - var->interpolation = ir_var_noperspective; - } else { - ir_read_error(expr, "unknown qualifier: %s", qualifier->value()); - return NULL; - } - } - - // Add the variable to the symbol table - state->symbols->add_variable(var); - - return var; -} - - -ir_if * -ir_reader::read_if(s_expression *expr, ir_loop *loop_ctx) -{ - s_expression *s_cond; - s_expression *s_then; - s_expression *s_else; - - s_pattern pat[] = { "if", s_cond, s_then, s_else }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (if (...) (...))"); - return NULL; - } - - ir_rvalue *condition = read_rvalue(s_cond); - if (condition == NULL) { - ir_read_error(NULL, "when reading condition of (if ...)"); - return NULL; - } - - ir_if *iff = new(mem_ctx) ir_if(condition); - - read_instructions(&iff->then_instructions, s_then, loop_ctx); - read_instructions(&iff->else_instructions, s_else, loop_ctx); - if (state->error) { - delete iff; - iff = NULL; - } - return iff; -} - - -ir_loop * -ir_reader::read_loop(s_expression *expr) -{ - s_expression *s_counter, *s_from, *s_to, *s_inc, *s_body; - - s_pattern pat[] = { "loop", s_counter, s_from, s_to, s_inc, s_body }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (loop " - " )"); - return NULL; - } - - // FINISHME: actually read the count/from/to fields. - - ir_loop *loop = new(mem_ctx) ir_loop; - read_instructions(&loop->body_instructions, s_body, loop); - if (state->error) { - delete loop; - loop = NULL; - } - return loop; -} - - -ir_return * -ir_reader::read_return(s_expression *expr) -{ - s_expression *s_retval; - - s_pattern pat[] = { "return", s_retval}; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (return )"); - return NULL; - } - - ir_rvalue *retval = read_rvalue(s_retval); - if (retval == NULL) { - ir_read_error(NULL, "when reading return value"); - return NULL; - } - - return new(mem_ctx) ir_return(retval); -} - - -ir_rvalue * -ir_reader::read_rvalue(s_expression *expr) -{ - s_list *list = SX_AS_LIST(expr); - if (list == NULL || list->subexpressions.is_empty()) - return NULL; - - s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.get_head()); - if (tag == NULL) { - ir_read_error(expr, "expected rvalue tag"); - return NULL; - } - - ir_rvalue *rvalue = read_dereference(list); - if (rvalue != NULL || state->error) - return rvalue; - else if (strcmp(tag->value(), "swiz") == 0) { - rvalue = read_swizzle(list); - } else if (strcmp(tag->value(), "expression") == 0) { - rvalue = read_expression(list); - } else if (strcmp(tag->value(), "call") == 0) { - rvalue = read_call(list); - } else if (strcmp(tag->value(), "constant") == 0) { - rvalue = read_constant(list); - } else { - rvalue = read_texture(list); - if (rvalue == NULL && !state->error) - ir_read_error(expr, "unrecognized rvalue tag: %s", tag->value()); - } - - return rvalue; -} - -ir_assignment * -ir_reader::read_assignment(s_expression *expr) -{ - s_expression *cond_expr = NULL; - s_expression *lhs_expr, *rhs_expr; - s_list *mask_list; - - s_pattern pat4[] = { "assign", mask_list, lhs_expr, rhs_expr }; - s_pattern pat5[] = { "assign", cond_expr, mask_list, lhs_expr, rhs_expr }; - if (!MATCH(expr, pat4) && !MATCH(expr, pat5)) { - ir_read_error(expr, "expected (assign [] () " - " )"); - return NULL; - } - - ir_rvalue *condition = NULL; - if (cond_expr != NULL) { - condition = read_rvalue(cond_expr); - if (condition == NULL) { - ir_read_error(NULL, "when reading condition of assignment"); - return NULL; - } - } - - unsigned mask = 0; - - s_symbol *mask_symbol; - s_pattern mask_pat[] = { mask_symbol }; - if (MATCH(mask_list, mask_pat)) { - const char *mask_str = mask_symbol->value(); - unsigned mask_length = strlen(mask_str); - if (mask_length > 4) { - ir_read_error(expr, "invalid write mask: %s", mask_str); - return NULL; - } - - const unsigned idx_map[] = { 3, 0, 1, 2 }; /* w=bit 3, x=0, y=1, z=2 */ - - for (unsigned i = 0; i < mask_length; i++) { - if (mask_str[i] < 'w' || mask_str[i] > 'z') { - ir_read_error(expr, "write mask contains invalid character: %c", - mask_str[i]); - return NULL; - } - mask |= 1 << idx_map[mask_str[i] - 'w']; - } - } else if (!mask_list->subexpressions.is_empty()) { - ir_read_error(mask_list, "expected () or ()"); - return NULL; - } - - ir_dereference *lhs = read_dereference(lhs_expr); - if (lhs == NULL) { - ir_read_error(NULL, "when reading left-hand side of assignment"); - return NULL; - } - - ir_rvalue *rhs = read_rvalue(rhs_expr); - if (rhs == NULL) { - ir_read_error(NULL, "when reading right-hand side of assignment"); - return NULL; - } - - if (mask == 0 && (lhs->type->is_vector() || lhs->type->is_scalar())) { - ir_read_error(expr, "non-zero write mask required."); - return NULL; - } - - return new(mem_ctx) ir_assignment(lhs, rhs, condition, mask); -} - -ir_call * -ir_reader::read_call(s_expression *expr) -{ - s_symbol *name; - s_list *params; - - s_pattern pat[] = { "call", name, params }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (call ( ...))"); - return NULL; - } - - exec_list parameters; - - foreach_iter(exec_list_iterator, it, params->subexpressions) { - s_expression *expr = (s_expression*) it.get(); - ir_rvalue *param = read_rvalue(expr); - if (param == NULL) { - ir_read_error(expr, "when reading parameter to function call"); - return NULL; - } - parameters.push_tail(param); - } - - ir_function *f = state->symbols->get_function(name->value()); - if (f == NULL) { - ir_read_error(expr, "found call to undefined function %s", - name->value()); - return NULL; - } - - ir_function_signature *callee = f->matching_signature(¶meters); - if (callee == NULL) { - ir_read_error(expr, "couldn't find matching signature for function " - "%s", name->value()); - return NULL; - } - - return new(mem_ctx) ir_call(callee, ¶meters); -} - -ir_expression * -ir_reader::read_expression(s_expression *expr) -{ - s_expression *s_type; - s_symbol *s_op; - s_expression *s_arg1; - - s_pattern pat[] = { "expression", s_type, s_op, s_arg1 }; - if (!PARTIAL_MATCH(expr, pat)) { - ir_read_error(expr, "expected (expression " - " [])"); - return NULL; - } - s_expression *s_arg2 = (s_expression *) s_arg1->next; // may be tail sentinel - - const glsl_type *type = read_type(s_type); - if (type == NULL) - return NULL; - - /* Read the operator */ - ir_expression_operation op = ir_expression::get_operator(s_op->value()); - if (op == (ir_expression_operation) -1) { - ir_read_error(expr, "invalid operator: %s", s_op->value()); - return NULL; - } - - unsigned num_operands = ir_expression::get_num_operands(op); - if (num_operands == 1 && !s_arg1->next->is_tail_sentinel()) { - ir_read_error(expr, "expected (expression %s )", - s_op->value()); - return NULL; - } - - ir_rvalue *arg1 = read_rvalue(s_arg1); - ir_rvalue *arg2 = NULL; - if (arg1 == NULL) { - ir_read_error(NULL, "when reading first operand of %s", s_op->value()); - return NULL; - } - - if (num_operands == 2) { - if (s_arg2->is_tail_sentinel() || !s_arg2->next->is_tail_sentinel()) { - ir_read_error(expr, "expected (expression %s " - ")", s_op->value()); - return NULL; - } - arg2 = read_rvalue(s_arg2); - if (arg2 == NULL) { - ir_read_error(NULL, "when reading second operand of %s", - s_op->value()); - return NULL; - } - } - - return new(mem_ctx) ir_expression(op, type, arg1, arg2); -} - -ir_swizzle * -ir_reader::read_swizzle(s_expression *expr) -{ - s_symbol *swiz; - s_expression *sub; - - s_pattern pat[] = { "swiz", swiz, sub }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (swiz )"); - return NULL; - } - - if (strlen(swiz->value()) > 4) { - ir_read_error(expr, "expected a valid swizzle; found %s", swiz->value()); - return NULL; - } - - ir_rvalue *rvalue = read_rvalue(sub); - if (rvalue == NULL) - return NULL; - - ir_swizzle *ir = ir_swizzle::create(rvalue, swiz->value(), - rvalue->type->vector_elements); - if (ir == NULL) - ir_read_error(expr, "invalid swizzle"); - - return ir; -} - -ir_constant * -ir_reader::read_constant(s_expression *expr) -{ - s_expression *type_expr; - s_list *values; - - s_pattern pat[] = { "constant", type_expr, values }; - if (!MATCH(expr, pat)) { - ir_read_error(expr, "expected (constant (...))"); - return NULL; - } - - const glsl_type *type = read_type(type_expr); - if (type == NULL) - return NULL; - - if (values == NULL) { - ir_read_error(expr, "expected (constant (...))"); - return NULL; - } - - if (type->is_array()) { - unsigned elements_supplied = 0; - exec_list elements; - foreach_iter(exec_list_iterator, it, values->subexpressions) { - s_expression *elt = (s_expression *) it.get(); - ir_constant *ir_elt = read_constant(elt); - if (ir_elt == NULL) - return NULL; - elements.push_tail(ir_elt); - elements_supplied++; - } - - if (elements_supplied != type->length) { - ir_read_error(values, "expected exactly %u array elements, " - "given %u", type->length, elements_supplied); - return NULL; - } - return new(mem_ctx) ir_constant(type, &elements); - } - - const glsl_type *const base_type = type->get_base_type(); - - ir_constant_data data = { { 0 } }; - - // Read in list of values (at most 16). - int k = 0; - foreach_iter(exec_list_iterator, it, values->subexpressions) { - if (k >= 16) { - ir_read_error(values, "expected at most 16 numbers"); - return NULL; - } - - s_expression *expr = (s_expression*) it.get(); - - if (base_type->base_type == GLSL_TYPE_FLOAT) { - s_number *value = SX_AS_NUMBER(expr); - if (value == NULL) { - ir_read_error(values, "expected numbers"); - return NULL; - } - data.f[k] = value->fvalue(); - } else { - s_int *value = SX_AS_INT(expr); - if (value == NULL) { - ir_read_error(values, "expected integers"); - return NULL; - } - - switch (base_type->base_type) { - case GLSL_TYPE_UINT: { - data.u[k] = value->value(); - break; - } - case GLSL_TYPE_INT: { - data.i[k] = value->value(); - break; - } - case GLSL_TYPE_BOOL: { - data.b[k] = value->value(); - break; - } - default: - ir_read_error(values, "unsupported constant type"); - return NULL; - } - } - ++k; - } - - return new(mem_ctx) ir_constant(type, &data); -} - -ir_dereference * -ir_reader::read_dereference(s_expression *expr) -{ - s_symbol *s_var; - s_expression *s_subject; - s_expression *s_index; - s_symbol *s_field; - - s_pattern var_pat[] = { "var_ref", s_var }; - s_pattern array_pat[] = { "array_ref", s_subject, s_index }; - s_pattern record_pat[] = { "record_ref", s_subject, s_field }; - - if (MATCH(expr, var_pat)) { - ir_variable *var = state->symbols->get_variable(s_var->value()); - if (var == NULL) { - ir_read_error(expr, "undeclared variable: %s", s_var->value()); - return NULL; - } - return new(mem_ctx) ir_dereference_variable(var); - } else if (MATCH(expr, array_pat)) { - ir_rvalue *subject = read_rvalue(s_subject); - if (subject == NULL) { - ir_read_error(NULL, "when reading the subject of an array_ref"); - return NULL; - } - - ir_rvalue *idx = read_rvalue(s_index); - if (subject == NULL) { - ir_read_error(NULL, "when reading the index of an array_ref"); - return NULL; - } - return new(mem_ctx) ir_dereference_array(subject, idx); - } else if (MATCH(expr, record_pat)) { - ir_rvalue *subject = read_rvalue(s_subject); - if (subject == NULL) { - ir_read_error(NULL, "when reading the subject of a record_ref"); - return NULL; - } - return new(mem_ctx) ir_dereference_record(subject, s_field->value()); - } - return NULL; -} - -ir_texture * -ir_reader::read_texture(s_expression *expr) -{ - s_symbol *tag = NULL; - s_expression *s_sampler = NULL; - s_expression *s_coord = NULL; - s_expression *s_offset = NULL; - s_expression *s_proj = NULL; - s_list *s_shadow = NULL; - s_expression *s_lod = NULL; - - ir_texture_opcode op = ir_tex; /* silence warning */ - - s_pattern tex_pattern[] = - { "tex", s_sampler, s_coord, s_offset, s_proj, s_shadow }; - s_pattern txf_pattern[] = - { "txf", s_sampler, s_coord, s_offset, s_lod }; - s_pattern other_pattern[] = - { tag, s_sampler, s_coord, s_offset, s_proj, s_shadow, s_lod }; - - if (MATCH(expr, tex_pattern)) { - op = ir_tex; - } else if (MATCH(expr, txf_pattern)) { - op = ir_txf; - } else if (MATCH(expr, other_pattern)) { - op = ir_texture::get_opcode(tag->value()); - if (op == -1) - return NULL; - } else { - ir_read_error(NULL, "unexpected texture pattern"); - return NULL; - } - - ir_texture *tex = new(mem_ctx) ir_texture(op); - - // Read sampler (must be a deref) - ir_dereference *sampler = read_dereference(s_sampler); - if (sampler == NULL) { - ir_read_error(NULL, "when reading sampler in (%s ...)", - tex->opcode_string()); - return NULL; - } - tex->set_sampler(sampler); - - // Read coordinate (any rvalue) - tex->coordinate = read_rvalue(s_coord); - if (tex->coordinate == NULL) { - ir_read_error(NULL, "when reading coordinate in (%s ...)", - tex->opcode_string()); - return NULL; - } - - // Read texel offset - either 0 or an rvalue. - s_int *si_offset = SX_AS_INT(s_offset); - if (si_offset == NULL || si_offset->value() != 0) { - tex->offset = read_rvalue(s_offset); - if (tex->offset == NULL) { - ir_read_error(s_offset, "expected 0 or an expression"); - return NULL; - } - } - - if (op != ir_txf) { - s_int *proj_as_int = SX_AS_INT(s_proj); - if (proj_as_int && proj_as_int->value() == 1) { - tex->projector = NULL; - } else { - tex->projector = read_rvalue(s_proj); - if (tex->projector == NULL) { - ir_read_error(NULL, "when reading projective divide in (%s ..)", - tex->opcode_string()); - return NULL; - } - } - - if (s_shadow->subexpressions.is_empty()) { - tex->shadow_comparitor = NULL; - } else { - tex->shadow_comparitor = read_rvalue(s_shadow); - if (tex->shadow_comparitor == NULL) { - ir_read_error(NULL, "when reading shadow comparitor in (%s ..)", - tex->opcode_string()); - return NULL; - } - } - } - - switch (op) { - case ir_txb: - tex->lod_info.bias = read_rvalue(s_lod); - if (tex->lod_info.bias == NULL) { - ir_read_error(NULL, "when reading LOD bias in (txb ...)"); - return NULL; - } - break; - case ir_txl: - case ir_txf: - tex->lod_info.lod = read_rvalue(s_lod); - if (tex->lod_info.lod == NULL) { - ir_read_error(NULL, "when reading LOD in (%s ...)", - tex->opcode_string()); - return NULL; - } - break; - case ir_txd: { - s_expression *s_dx, *s_dy; - s_pattern dxdy_pat[] = { s_dx, s_dy }; - if (!MATCH(s_lod, dxdy_pat)) { - ir_read_error(s_lod, "expected (dPdx dPdy) in (txd ...)"); - return NULL; - } - tex->lod_info.grad.dPdx = read_rvalue(s_dx); - if (tex->lod_info.grad.dPdx == NULL) { - ir_read_error(NULL, "when reading dPdx in (txd ...)"); - return NULL; - } - tex->lod_info.grad.dPdy = read_rvalue(s_dy); - if (tex->lod_info.grad.dPdy == NULL) { - ir_read_error(NULL, "when reading dPdy in (txd ...)"); - return NULL; - } - break; - } - default: - // tex doesn't have any extra parameters. - break; - }; - return tex; -} +/* + * 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_reader.h" +#include "glsl_parser_extras.h" +#include "glsl_types.h" +#include "s_expression.h" + +const static bool debug = false; + +class ir_reader { +public: + ir_reader(_mesa_glsl_parse_state *); + + void read(exec_list *instructions, const char *src, bool scan_for_protos); + +private: + void *mem_ctx; + _mesa_glsl_parse_state *state; + + void ir_read_error(s_expression *, const char *fmt, ...); + + const glsl_type *read_type(s_expression *); + + void scan_for_prototypes(exec_list *, s_expression *); + ir_function *read_function(s_expression *, bool skip_body); + void read_function_sig(ir_function *, s_expression *, bool skip_body); + + void read_instructions(exec_list *, s_expression *, ir_loop *); + ir_instruction *read_instruction(s_expression *, ir_loop *); + ir_variable *read_declaration(s_expression *); + ir_if *read_if(s_expression *, ir_loop *); + ir_loop *read_loop(s_expression *); + ir_return *read_return(s_expression *); + ir_rvalue *read_rvalue(s_expression *); + ir_assignment *read_assignment(s_expression *); + ir_expression *read_expression(s_expression *); + ir_call *read_call(s_expression *); + ir_swizzle *read_swizzle(s_expression *); + ir_constant *read_constant(s_expression *); + ir_texture *read_texture(s_expression *); + + ir_dereference *read_dereference(s_expression *); +}; + +ir_reader::ir_reader(_mesa_glsl_parse_state *state) : state(state) +{ + this->mem_ctx = state; +} + +void +_mesa_glsl_read_ir(_mesa_glsl_parse_state *state, exec_list *instructions, + const char *src, bool scan_for_protos) +{ + ir_reader r(state); + r.read(instructions, src, scan_for_protos); +} + +void +ir_reader::read(exec_list *instructions, const char *src, bool scan_for_protos) +{ + s_expression *expr = s_expression::read_expression(mem_ctx, src); + if (expr == NULL) { + ir_read_error(NULL, "couldn't parse S-Expression."); + return; + } + + if (scan_for_protos) { + scan_for_prototypes(instructions, expr); + if (state->error) + return; + } + + read_instructions(instructions, expr, NULL); + ralloc_free(expr); + + if (debug) + validate_ir_tree(instructions); +} + +void +ir_reader::ir_read_error(s_expression *expr, const char *fmt, ...) +{ + va_list ap; + + state->error = true; + + if (state->current_function != NULL) + ralloc_asprintf_append(&state->info_log, "In function %s:\n", + state->current_function->function_name()); + ralloc_strcat(&state->info_log, "error: "); + + va_start(ap, fmt); + ralloc_vasprintf_append(&state->info_log, fmt, ap); + va_end(ap); + ralloc_strcat(&state->info_log, "\n"); + + if (expr != NULL) { + ralloc_strcat(&state->info_log, "...in this context:\n "); + expr->print(); + ralloc_strcat(&state->info_log, "\n\n"); + } +} + +const glsl_type * +ir_reader::read_type(s_expression *expr) +{ + s_expression *s_base_type; + s_int *s_size; + + s_pattern pat[] = { "array", s_base_type, s_size }; + if (MATCH(expr, pat)) { + const glsl_type *base_type = read_type(s_base_type); + if (base_type == NULL) { + ir_read_error(NULL, "when reading base type of array type"); + return NULL; + } + + return glsl_type::get_array_instance(base_type, s_size->value()); + } + + s_symbol *type_sym = SX_AS_SYMBOL(expr); + if (type_sym == NULL) { + ir_read_error(expr, "expected "); + return NULL; + } + + const glsl_type *type = state->symbols->get_type(type_sym->value()); + if (type == NULL) + ir_read_error(expr, "invalid type: %s", type_sym->value()); + + return type; +} + + +void +ir_reader::scan_for_prototypes(exec_list *instructions, s_expression *expr) +{ + s_list *list = SX_AS_LIST(expr); + if (list == NULL) { + ir_read_error(expr, "Expected ( ...); found an atom."); + return; + } + + foreach_iter(exec_list_iterator, it, list->subexpressions) { + s_list *sub = SX_AS_LIST(it.get()); + if (sub == NULL) + continue; // not a (function ...); ignore it. + + s_symbol *tag = SX_AS_SYMBOL(sub->subexpressions.get_head()); + if (tag == NULL || strcmp(tag->value(), "function") != 0) + continue; // not a (function ...); ignore it. + + ir_function *f = read_function(sub, true); + if (f == NULL) + return; + instructions->push_tail(f); + } +} + +ir_function * +ir_reader::read_function(s_expression *expr, bool skip_body) +{ + bool added = false; + s_symbol *name; + + s_pattern pat[] = { "function", name }; + if (!PARTIAL_MATCH(expr, pat)) { + ir_read_error(expr, "Expected (function (signature ...) ...)"); + return NULL; + } + + ir_function *f = state->symbols->get_function(name->value()); + if (f == NULL) { + f = new(mem_ctx) ir_function(name->value()); + added = state->symbols->add_function(f); + assert(added); + } + + exec_list_iterator it = ((s_list *) expr)->subexpressions.iterator(); + it.next(); // skip "function" tag + it.next(); // skip function name + for (/* nothing */; it.has_next(); it.next()) { + s_expression *s_sig = (s_expression *) it.get(); + read_function_sig(f, s_sig, skip_body); + } + return added ? f : NULL; +} + +void +ir_reader::read_function_sig(ir_function *f, s_expression *expr, bool skip_body) +{ + s_expression *type_expr; + s_list *paramlist; + s_list *body_list; + + s_pattern pat[] = { "signature", type_expr, paramlist, body_list }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "Expected (signature (parameters ...) " + "( ...))"); + return; + } + + const glsl_type *return_type = read_type(type_expr); + if (return_type == NULL) + return; + + s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head()); + if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) { + ir_read_error(paramlist, "Expected (parameters ...)"); + return; + } + + // Read the parameters list into a temporary place. + exec_list hir_parameters; + state->symbols->push_scope(); + + exec_list_iterator it = paramlist->subexpressions.iterator(); + for (it.next() /* skip "parameters" */; it.has_next(); it.next()) { + ir_variable *var = read_declaration((s_expression *) it.get()); + if (var == NULL) + return; + + hir_parameters.push_tail(var); + } + + ir_function_signature *sig = f->exact_matching_signature(&hir_parameters); + if (sig == NULL && skip_body) { + /* If scanning for prototypes, generate a new signature. */ + sig = new(mem_ctx) ir_function_signature(return_type); + sig->is_builtin = true; + f->add_signature(sig); + } else if (sig != NULL) { + const char *badvar = sig->qualifiers_match(&hir_parameters); + if (badvar != NULL) { + ir_read_error(expr, "function `%s' parameter `%s' qualifiers " + "don't match prototype", f->name, badvar); + return; + } + + if (sig->return_type != return_type) { + ir_read_error(expr, "function `%s' return type doesn't " + "match prototype", f->name); + return; + } + } else { + /* No prototype for this body exists - skip it. */ + state->symbols->pop_scope(); + return; + } + assert(sig != NULL); + + sig->replace_parameters(&hir_parameters); + + if (!skip_body && !body_list->subexpressions.is_empty()) { + if (sig->is_defined) { + ir_read_error(expr, "function %s redefined", f->name); + return; + } + state->current_function = sig; + read_instructions(&sig->body, body_list, NULL); + state->current_function = NULL; + sig->is_defined = true; + } + + state->symbols->pop_scope(); +} + +void +ir_reader::read_instructions(exec_list *instructions, s_expression *expr, + ir_loop *loop_ctx) +{ + // Read in a list of instructions + s_list *list = SX_AS_LIST(expr); + if (list == NULL) { + ir_read_error(expr, "Expected ( ...); found an atom."); + return; + } + + foreach_iter(exec_list_iterator, it, list->subexpressions) { + s_expression *sub = (s_expression*) it.get(); + ir_instruction *ir = read_instruction(sub, loop_ctx); + if (ir != NULL) { + /* Global variable declarations should be moved to the top, before + * any functions that might use them. Functions are added to the + * instruction stream when scanning for prototypes, so without this + * hack, they always appear before variable declarations. + */ + if (state->current_function == NULL && ir->as_variable() != NULL) + instructions->push_head(ir); + else + instructions->push_tail(ir); + } + } +} + + +ir_instruction * +ir_reader::read_instruction(s_expression *expr, ir_loop *loop_ctx) +{ + s_symbol *symbol = SX_AS_SYMBOL(expr); + if (symbol != NULL) { + if (strcmp(symbol->value(), "break") == 0 && loop_ctx != NULL) + return new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_break); + if (strcmp(symbol->value(), "continue") == 0 && loop_ctx != NULL) + return new(mem_ctx) ir_loop_jump(ir_loop_jump::jump_continue); + } + + s_list *list = SX_AS_LIST(expr); + if (list == NULL || list->subexpressions.is_empty()) { + ir_read_error(expr, "Invalid instruction.\n"); + return NULL; + } + + s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.get_head()); + if (tag == NULL) { + ir_read_error(expr, "expected instruction tag"); + return NULL; + } + + ir_instruction *inst = NULL; + if (strcmp(tag->value(), "declare") == 0) { + inst = read_declaration(list); + } else if (strcmp(tag->value(), "assign") == 0) { + inst = read_assignment(list); + } else if (strcmp(tag->value(), "if") == 0) { + inst = read_if(list, loop_ctx); + } else if (strcmp(tag->value(), "loop") == 0) { + inst = read_loop(list); + } else if (strcmp(tag->value(), "return") == 0) { + inst = read_return(list); + } else if (strcmp(tag->value(), "function") == 0) { + inst = read_function(list, false); + } else { + inst = read_rvalue(list); + if (inst == NULL) + ir_read_error(NULL, "when reading instruction"); + } + return inst; +} + +ir_variable * +ir_reader::read_declaration(s_expression *expr) +{ + s_list *s_quals; + s_expression *s_type; + s_symbol *s_name; + + s_pattern pat[] = { "declare", s_quals, s_type, s_name }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (declare () )"); + return NULL; + } + + const glsl_type *type = read_type(s_type); + if (type == NULL) + return NULL; + + ir_variable *var = new(mem_ctx) ir_variable(type, s_name->value(), + ir_var_auto); + + foreach_iter(exec_list_iterator, it, s_quals->subexpressions) { + s_symbol *qualifier = SX_AS_SYMBOL(it.get()); + if (qualifier == NULL) { + ir_read_error(expr, "qualifier list must contain only symbols"); + return NULL; + } + + // FINISHME: Check for duplicate/conflicting qualifiers. + if (strcmp(qualifier->value(), "centroid") == 0) { + var->centroid = 1; + } else if (strcmp(qualifier->value(), "invariant") == 0) { + var->invariant = 1; + } else if (strcmp(qualifier->value(), "uniform") == 0) { + var->mode = ir_var_uniform; + } else if (strcmp(qualifier->value(), "auto") == 0) { + var->mode = ir_var_auto; + } else if (strcmp(qualifier->value(), "in") == 0) { + var->mode = ir_var_in; + } else if (strcmp(qualifier->value(), "const_in") == 0) { + var->mode = ir_var_const_in; + } else if (strcmp(qualifier->value(), "out") == 0) { + var->mode = ir_var_out; + } else if (strcmp(qualifier->value(), "inout") == 0) { + var->mode = ir_var_inout; + } else if (strcmp(qualifier->value(), "smooth") == 0) { + var->interpolation = ir_var_smooth; + } else if (strcmp(qualifier->value(), "flat") == 0) { + var->interpolation = ir_var_flat; + } else if (strcmp(qualifier->value(), "noperspective") == 0) { + var->interpolation = ir_var_noperspective; + } else { + ir_read_error(expr, "unknown qualifier: %s", qualifier->value()); + return NULL; + } + } + + // Add the variable to the symbol table + state->symbols->add_variable(var); + + return var; +} + + +ir_if * +ir_reader::read_if(s_expression *expr, ir_loop *loop_ctx) +{ + s_expression *s_cond; + s_expression *s_then; + s_expression *s_else; + + s_pattern pat[] = { "if", s_cond, s_then, s_else }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (if (...) (...))"); + return NULL; + } + + ir_rvalue *condition = read_rvalue(s_cond); + if (condition == NULL) { + ir_read_error(NULL, "when reading condition of (if ...)"); + return NULL; + } + + ir_if *iff = new(mem_ctx) ir_if(condition); + + read_instructions(&iff->then_instructions, s_then, loop_ctx); + read_instructions(&iff->else_instructions, s_else, loop_ctx); + if (state->error) { + delete iff; + iff = NULL; + } + return iff; +} + + +ir_loop * +ir_reader::read_loop(s_expression *expr) +{ + s_expression *s_counter, *s_from, *s_to, *s_inc, *s_body; + + s_pattern pat[] = { "loop", s_counter, s_from, s_to, s_inc, s_body }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (loop " + " )"); + return NULL; + } + + // FINISHME: actually read the count/from/to fields. + + ir_loop *loop = new(mem_ctx) ir_loop; + read_instructions(&loop->body_instructions, s_body, loop); + if (state->error) { + delete loop; + loop = NULL; + } + return loop; +} + + +ir_return * +ir_reader::read_return(s_expression *expr) +{ + s_expression *s_retval; + + s_pattern pat[] = { "return", s_retval}; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (return )"); + return NULL; + } + + ir_rvalue *retval = read_rvalue(s_retval); + if (retval == NULL) { + ir_read_error(NULL, "when reading return value"); + return NULL; + } + + return new(mem_ctx) ir_return(retval); +} + + +ir_rvalue * +ir_reader::read_rvalue(s_expression *expr) +{ + s_list *list = SX_AS_LIST(expr); + if (list == NULL || list->subexpressions.is_empty()) + return NULL; + + s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.get_head()); + if (tag == NULL) { + ir_read_error(expr, "expected rvalue tag"); + return NULL; + } + + ir_rvalue *rvalue = read_dereference(list); + if (rvalue != NULL || state->error) + return rvalue; + else if (strcmp(tag->value(), "swiz") == 0) { + rvalue = read_swizzle(list); + } else if (strcmp(tag->value(), "expression") == 0) { + rvalue = read_expression(list); + } else if (strcmp(tag->value(), "call") == 0) { + rvalue = read_call(list); + } else if (strcmp(tag->value(), "constant") == 0) { + rvalue = read_constant(list); + } else { + rvalue = read_texture(list); + if (rvalue == NULL && !state->error) + ir_read_error(expr, "unrecognized rvalue tag: %s", tag->value()); + } + + return rvalue; +} + +ir_assignment * +ir_reader::read_assignment(s_expression *expr) +{ + s_expression *cond_expr = NULL; + s_expression *lhs_expr, *rhs_expr; + s_list *mask_list; + + s_pattern pat4[] = { "assign", mask_list, lhs_expr, rhs_expr }; + s_pattern pat5[] = { "assign", cond_expr, mask_list, lhs_expr, rhs_expr }; + if (!MATCH(expr, pat4) && !MATCH(expr, pat5)) { + ir_read_error(expr, "expected (assign [] () " + " )"); + return NULL; + } + + ir_rvalue *condition = NULL; + if (cond_expr != NULL) { + condition = read_rvalue(cond_expr); + if (condition == NULL) { + ir_read_error(NULL, "when reading condition of assignment"); + return NULL; + } + } + + unsigned mask = 0; + + s_symbol *mask_symbol; + s_pattern mask_pat[] = { mask_symbol }; + if (MATCH(mask_list, mask_pat)) { + const char *mask_str = mask_symbol->value(); + unsigned mask_length = strlen(mask_str); + if (mask_length > 4) { + ir_read_error(expr, "invalid write mask: %s", mask_str); + return NULL; + } + + const unsigned idx_map[] = { 3, 0, 1, 2 }; /* w=bit 3, x=0, y=1, z=2 */ + + for (unsigned i = 0; i < mask_length; i++) { + if (mask_str[i] < 'w' || mask_str[i] > 'z') { + ir_read_error(expr, "write mask contains invalid character: %c", + mask_str[i]); + return NULL; + } + mask |= 1 << idx_map[mask_str[i] - 'w']; + } + } else if (!mask_list->subexpressions.is_empty()) { + ir_read_error(mask_list, "expected () or ()"); + return NULL; + } + + ir_dereference *lhs = read_dereference(lhs_expr); + if (lhs == NULL) { + ir_read_error(NULL, "when reading left-hand side of assignment"); + return NULL; + } + + ir_rvalue *rhs = read_rvalue(rhs_expr); + if (rhs == NULL) { + ir_read_error(NULL, "when reading right-hand side of assignment"); + return NULL; + } + + if (mask == 0 && (lhs->type->is_vector() || lhs->type->is_scalar())) { + ir_read_error(expr, "non-zero write mask required."); + return NULL; + } + + return new(mem_ctx) ir_assignment(lhs, rhs, condition, mask); +} + +ir_call * +ir_reader::read_call(s_expression *expr) +{ + s_symbol *name; + s_list *params; + + s_pattern pat[] = { "call", name, params }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (call ( ...))"); + return NULL; + } + + exec_list parameters; + + foreach_iter(exec_list_iterator, it, params->subexpressions) { + s_expression *expr = (s_expression*) it.get(); + ir_rvalue *param = read_rvalue(expr); + if (param == NULL) { + ir_read_error(expr, "when reading parameter to function call"); + return NULL; + } + parameters.push_tail(param); + } + + ir_function *f = state->symbols->get_function(name->value()); + if (f == NULL) { + ir_read_error(expr, "found call to undefined function %s", + name->value()); + return NULL; + } + + ir_function_signature *callee = f->matching_signature(¶meters); + if (callee == NULL) { + ir_read_error(expr, "couldn't find matching signature for function " + "%s", name->value()); + return NULL; + } + + return new(mem_ctx) ir_call(callee, ¶meters); +} + +ir_expression * +ir_reader::read_expression(s_expression *expr) +{ + s_expression *s_type; + s_symbol *s_op; + s_expression *s_arg1; + + s_pattern pat[] = { "expression", s_type, s_op, s_arg1 }; + if (!PARTIAL_MATCH(expr, pat)) { + ir_read_error(expr, "expected (expression " + " [])"); + return NULL; + } + s_expression *s_arg2 = (s_expression *) s_arg1->next; // may be tail sentinel + + const glsl_type *type = read_type(s_type); + if (type == NULL) + return NULL; + + /* Read the operator */ + ir_expression_operation op = ir_expression::get_operator(s_op->value()); + if (op == (ir_expression_operation) -1) { + ir_read_error(expr, "invalid operator: %s", s_op->value()); + return NULL; + } + + unsigned num_operands = ir_expression::get_num_operands(op); + if (num_operands == 1 && !s_arg1->next->is_tail_sentinel()) { + ir_read_error(expr, "expected (expression %s )", + s_op->value()); + return NULL; + } + + ir_rvalue *arg1 = read_rvalue(s_arg1); + ir_rvalue *arg2 = NULL; + if (arg1 == NULL) { + ir_read_error(NULL, "when reading first operand of %s", s_op->value()); + return NULL; + } + + if (num_operands == 2) { + if (s_arg2->is_tail_sentinel() || !s_arg2->next->is_tail_sentinel()) { + ir_read_error(expr, "expected (expression %s " + ")", s_op->value()); + return NULL; + } + arg2 = read_rvalue(s_arg2); + if (arg2 == NULL) { + ir_read_error(NULL, "when reading second operand of %s", + s_op->value()); + return NULL; + } + } + + return new(mem_ctx) ir_expression(op, type, arg1, arg2); +} + +ir_swizzle * +ir_reader::read_swizzle(s_expression *expr) +{ + s_symbol *swiz; + s_expression *sub; + + s_pattern pat[] = { "swiz", swiz, sub }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (swiz )"); + return NULL; + } + + if (strlen(swiz->value()) > 4) { + ir_read_error(expr, "expected a valid swizzle; found %s", swiz->value()); + return NULL; + } + + ir_rvalue *rvalue = read_rvalue(sub); + if (rvalue == NULL) + return NULL; + + ir_swizzle *ir = ir_swizzle::create(rvalue, swiz->value(), + rvalue->type->vector_elements); + if (ir == NULL) + ir_read_error(expr, "invalid swizzle"); + + return ir; +} + +ir_constant * +ir_reader::read_constant(s_expression *expr) +{ + s_expression *type_expr; + s_list *values; + + s_pattern pat[] = { "constant", type_expr, values }; + if (!MATCH(expr, pat)) { + ir_read_error(expr, "expected (constant (...))"); + return NULL; + } + + const glsl_type *type = read_type(type_expr); + if (type == NULL) + return NULL; + + if (values == NULL) { + ir_read_error(expr, "expected (constant (...))"); + return NULL; + } + + if (type->is_array()) { + unsigned elements_supplied = 0; + exec_list elements; + foreach_iter(exec_list_iterator, it, values->subexpressions) { + s_expression *elt = (s_expression *) it.get(); + ir_constant *ir_elt = read_constant(elt); + if (ir_elt == NULL) + return NULL; + elements.push_tail(ir_elt); + elements_supplied++; + } + + if (elements_supplied != type->length) { + ir_read_error(values, "expected exactly %u array elements, " + "given %u", type->length, elements_supplied); + return NULL; + } + return new(mem_ctx) ir_constant(type, &elements); + } + + const glsl_type *const base_type = type->get_base_type(); + + ir_constant_data data = { { 0 } }; + + // Read in list of values (at most 16). + int k = 0; + foreach_iter(exec_list_iterator, it, values->subexpressions) { + if (k >= 16) { + ir_read_error(values, "expected at most 16 numbers"); + return NULL; + } + + s_expression *expr = (s_expression*) it.get(); + + if (base_type->base_type == GLSL_TYPE_FLOAT) { + s_number *value = SX_AS_NUMBER(expr); + if (value == NULL) { + ir_read_error(values, "expected numbers"); + return NULL; + } + data.f[k] = value->fvalue(); + } else { + s_int *value = SX_AS_INT(expr); + if (value == NULL) { + ir_read_error(values, "expected integers"); + return NULL; + } + + switch (base_type->base_type) { + case GLSL_TYPE_UINT: { + data.u[k] = value->value(); + break; + } + case GLSL_TYPE_INT: { + data.i[k] = value->value(); + break; + } + case GLSL_TYPE_BOOL: { + data.b[k] = value->value(); + break; + } + default: + ir_read_error(values, "unsupported constant type"); + return NULL; + } + } + ++k; + } + + return new(mem_ctx) ir_constant(type, &data); +} + +ir_dereference * +ir_reader::read_dereference(s_expression *expr) +{ + s_symbol *s_var; + s_expression *s_subject; + s_expression *s_index; + s_symbol *s_field; + + s_pattern var_pat[] = { "var_ref", s_var }; + s_pattern array_pat[] = { "array_ref", s_subject, s_index }; + s_pattern record_pat[] = { "record_ref", s_subject, s_field }; + + if (MATCH(expr, var_pat)) { + ir_variable *var = state->symbols->get_variable(s_var->value()); + if (var == NULL) { + ir_read_error(expr, "undeclared variable: %s", s_var->value()); + return NULL; + } + return new(mem_ctx) ir_dereference_variable(var); + } else if (MATCH(expr, array_pat)) { + ir_rvalue *subject = read_rvalue(s_subject); + if (subject == NULL) { + ir_read_error(NULL, "when reading the subject of an array_ref"); + return NULL; + } + + ir_rvalue *idx = read_rvalue(s_index); + if (subject == NULL) { + ir_read_error(NULL, "when reading the index of an array_ref"); + return NULL; + } + return new(mem_ctx) ir_dereference_array(subject, idx); + } else if (MATCH(expr, record_pat)) { + ir_rvalue *subject = read_rvalue(s_subject); + if (subject == NULL) { + ir_read_error(NULL, "when reading the subject of a record_ref"); + return NULL; + } + return new(mem_ctx) ir_dereference_record(subject, s_field->value()); + } + return NULL; +} + +ir_texture * +ir_reader::read_texture(s_expression *expr) +{ + s_symbol *tag = NULL; + s_expression *s_type = NULL; + s_expression *s_sampler = NULL; + s_expression *s_coord = NULL; + s_expression *s_offset = NULL; + s_expression *s_proj = NULL; + s_list *s_shadow = NULL; + s_expression *s_lod = NULL; + + ir_texture_opcode op = ir_tex; /* silence warning */ + + s_pattern tex_pattern[] = + { "tex", s_type, s_sampler, s_coord, s_offset, s_proj, s_shadow }; + s_pattern txf_pattern[] = + { "txf", s_type, s_sampler, s_coord, s_offset, s_lod }; + s_pattern other_pattern[] = + { tag, s_type, s_sampler, s_coord, s_offset, s_proj, s_shadow, s_lod }; + + if (MATCH(expr, tex_pattern)) { + op = ir_tex; + } else if (MATCH(expr, txf_pattern)) { + op = ir_txf; + } else if (MATCH(expr, other_pattern)) { + op = ir_texture::get_opcode(tag->value()); + if (op == -1) + return NULL; + } else { + ir_read_error(NULL, "unexpected texture pattern"); + return NULL; + } + + ir_texture *tex = new(mem_ctx) ir_texture(op); + + // Read return type + const glsl_type *type = read_type(s_type); + if (type == NULL) { + ir_read_error(NULL, "when reading type in (%s ...)", + tex->opcode_string()); + return NULL; + } + + // Read sampler (must be a deref) + ir_dereference *sampler = read_dereference(s_sampler); + if (sampler == NULL) { + ir_read_error(NULL, "when reading sampler in (%s ...)", + tex->opcode_string()); + return NULL; + } + tex->set_sampler(sampler, type); + + // Read coordinate (any rvalue) + tex->coordinate = read_rvalue(s_coord); + if (tex->coordinate == NULL) { + ir_read_error(NULL, "when reading coordinate in (%s ...)", + tex->opcode_string()); + return NULL; + } + + // Read texel offset - either 0 or an rvalue. + s_int *si_offset = SX_AS_INT(s_offset); + if (si_offset == NULL || si_offset->value() != 0) { + tex->offset = read_rvalue(s_offset); + if (tex->offset == NULL) { + ir_read_error(s_offset, "expected 0 or an expression"); + return NULL; + } + } + + if (op != ir_txf) { + s_int *proj_as_int = SX_AS_INT(s_proj); + if (proj_as_int && proj_as_int->value() == 1) { + tex->projector = NULL; + } else { + tex->projector = read_rvalue(s_proj); + if (tex->projector == NULL) { + ir_read_error(NULL, "when reading projective divide in (%s ..)", + tex->opcode_string()); + return NULL; + } + } + + if (s_shadow->subexpressions.is_empty()) { + tex->shadow_comparitor = NULL; + } else { + tex->shadow_comparitor = read_rvalue(s_shadow); + if (tex->shadow_comparitor == NULL) { + ir_read_error(NULL, "when reading shadow comparitor in (%s ..)", + tex->opcode_string()); + return NULL; + } + } + } + + switch (op) { + case ir_txb: + tex->lod_info.bias = read_rvalue(s_lod); + if (tex->lod_info.bias == NULL) { + ir_read_error(NULL, "when reading LOD bias in (txb ...)"); + return NULL; + } + break; + case ir_txl: + case ir_txf: + tex->lod_info.lod = read_rvalue(s_lod); + if (tex->lod_info.lod == NULL) { + ir_read_error(NULL, "when reading LOD in (%s ...)", + tex->opcode_string()); + return NULL; + } + break; + case ir_txd: { + s_expression *s_dx, *s_dy; + s_pattern dxdy_pat[] = { s_dx, s_dy }; + if (!MATCH(s_lod, dxdy_pat)) { + ir_read_error(s_lod, "expected (dPdx dPdy) in (txd ...)"); + return NULL; + } + tex->lod_info.grad.dPdx = read_rvalue(s_dx); + if (tex->lod_info.grad.dPdx == NULL) { + ir_read_error(NULL, "when reading dPdx in (txd ...)"); + return NULL; + } + tex->lod_info.grad.dPdy = read_rvalue(s_dy); + if (tex->lod_info.grad.dPdy == NULL) { + ir_read_error(NULL, "when reading dPdy in (txd ...)"); + return NULL; + } + break; + } + default: + // tex doesn't have any extra parameters. + break; + }; + return tex; +} diff --git a/mesalib/src/glsl/opt_dead_functions.cpp b/mesalib/src/glsl/opt_dead_functions.cpp index ceb79080a..3c3eb8904 100644 --- a/mesalib/src/glsl/opt_dead_functions.cpp +++ b/mesalib/src/glsl/opt_dead_functions.cpp @@ -1,153 +1,164 @@ - /* - * 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_functions.cpp - * - * Eliminates unused functions from the linked program. - */ - - #include "ir.h" - #include "ir_visitor.h" - #include "ir_expression_flattening.h" - #include "glsl_types.h" - - class signature_entry : public exec_node - { - public: - signature_entry(ir_function_signature *sig) - { - this->signature = sig; - this->used = false; - } - - ir_function_signature *signature; - bool used; - }; - - class ir_dead_functions_visitor : public ir_hierarchical_visitor { - public: - ir_dead_functions_visitor() - { - this->mem_ctx = ralloc_context(NULL); - } - - ~ir_dead_functions_visitor() - { - ralloc_free(this->mem_ctx); - } - - virtual ir_visitor_status visit_enter(ir_function_signature *); - virtual ir_visitor_status visit_enter(ir_call *); - - signature_entry *get_signature_entry(ir_function_signature *var); - - bool (*predicate)(ir_instruction *ir); - - /* List of signature_entry */ - exec_list signature_list; - void *mem_ctx; - }; - - - signature_entry * - ir_dead_functions_visitor::get_signature_entry(ir_function_signature *sig) - { - foreach_iter(exec_list_iterator, iter, this->signature_list) { - signature_entry *entry = (signature_entry *)iter.get(); - if (entry->signature == sig) - return entry; - } - - signature_entry *entry = new(mem_ctx) signature_entry(sig); - this->signature_list.push_tail(entry); - return entry; - } - - - ir_visitor_status - ir_dead_functions_visitor::visit_enter(ir_function_signature *ir) - { - signature_entry *entry = this->get_signature_entry(ir); - - if (strcmp(ir->function_name(), "main") == 0) { - entry->used = true; - } - - return visit_continue; - } - - - ir_visitor_status - ir_dead_functions_visitor::visit_enter(ir_call *ir) - { - signature_entry *entry = this->get_signature_entry(ir->get_callee()); - - entry->used = true; - - return visit_continue; -} - -bool -do_dead_functions(exec_list *instructions) -{ - ir_dead_functions_visitor v; - bool progress = false; - - visit_list_elements(&v, instructions); - - /* Now that we've figured out which function signatures are used, remove - * the unused ones, and remove function definitions that have no more - * signatures. - */ - foreach_iter(exec_list_iterator, iter, v.signature_list) { - signature_entry *entry = (signature_entry *)iter.get(); - - if (!entry->used) { - entry->signature->remove(); - delete entry->signature; - progress = true; - } - delete(entry); - } - - /* We don't just do this above when we nuked a signature because of - * const pointers. - */ - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_function *func = ir->as_function(); - - if (func && func->signatures.is_empty()) { - /* At this point (post-linking), the symbol table is no - * longer in use, so not removing the function from the - * symbol table should be OK. - */ - func->remove(); - delete func; - 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_functions.cpp + * + * Eliminates unused functions from the linked program. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_expression_flattening.h" +#include "glsl_types.h" + +class signature_entry : public exec_node +{ +public: + signature_entry(ir_function_signature *sig) + { + this->signature = sig; + this->used = false; + } + + ir_function_signature *signature; + bool used; +}; + +class ir_dead_functions_visitor : public ir_hierarchical_visitor { +public: + ir_dead_functions_visitor() + { + this->mem_ctx = ralloc_context(NULL); + this->seen_another_function_signature = false; + } + + ~ir_dead_functions_visitor() + { + ralloc_free(this->mem_ctx); + } + + virtual ir_visitor_status visit_enter(ir_function_signature *); + virtual ir_visitor_status visit_enter(ir_call *); + + signature_entry *get_signature_entry(ir_function_signature *var); + + bool (*predicate)(ir_instruction *ir); + + bool seen_another_function_signature; + + /* List of signature_entry */ + exec_list signature_list; + void *mem_ctx; +}; + + +signature_entry * +ir_dead_functions_visitor::get_signature_entry(ir_function_signature *sig) +{ + foreach_iter(exec_list_iterator, iter, this->signature_list) { + signature_entry *entry = (signature_entry *)iter.get(); + if (entry->signature == sig) + return entry; + } + + signature_entry *entry = new(mem_ctx) signature_entry(sig); + this->signature_list.push_tail(entry); + return entry; +} + + +ir_visitor_status +ir_dead_functions_visitor::visit_enter(ir_function_signature *ir) +{ + signature_entry *entry = this->get_signature_entry(ir); + + if (strcmp(ir->function_name(), "main") == 0) { + entry->used = true; + } + + /* If this is the first signature to look at, no need to descend to see + * if it has calls to another function signature. + */ + if (!this->seen_another_function_signature) { + this->seen_another_function_signature = true; + return visit_continue_with_parent; + } + + return visit_continue; +} + + +ir_visitor_status +ir_dead_functions_visitor::visit_enter(ir_call *ir) +{ + signature_entry *entry = this->get_signature_entry(ir->get_callee()); + + entry->used = true; + + return visit_continue; +} + +bool +do_dead_functions(exec_list *instructions) +{ + ir_dead_functions_visitor v; + bool progress = false; + + visit_list_elements(&v, instructions); + + /* Now that we've figured out which function signatures are used, remove + * the unused ones, and remove function definitions that have no more + * signatures. + */ + foreach_iter(exec_list_iterator, iter, v.signature_list) { + signature_entry *entry = (signature_entry *)iter.get(); + + if (!entry->used) { + entry->signature->remove(); + delete entry->signature; + progress = true; + } + delete(entry); + } + + /* We don't just do this above when we nuked a signature because of + * const pointers. + */ + foreach_iter(exec_list_iterator, iter, *instructions) { + ir_instruction *ir = (ir_instruction *)iter.get(); + ir_function *func = ir->as_function(); + + if (func && func->signatures.is_empty()) { + /* At this point (post-linking), the symbol table is no + * longer in use, so not removing the function from the + * symbol table should be OK. + */ + func->remove(); + delete func; + progress = true; + } + } + + return progress; +} diff --git a/mesalib/src/glsl/opt_discard_simplification.cpp b/mesalib/src/glsl/opt_discard_simplification.cpp index df8caba4d..37622eb02 100644 --- a/mesalib/src/glsl/opt_discard_simplification.cpp +++ b/mesalib/src/glsl/opt_discard_simplification.cpp @@ -95,6 +95,7 @@ public: ir_visitor_status visit_enter(ir_if *); ir_visitor_status visit_enter(ir_loop *); + ir_visitor_status visit_enter(ir_assignment *); bool progress; }; @@ -117,6 +118,15 @@ is_only_instruction(ir_discard *discard) discard->next->is_tail_sentinel()); } +/* We only care about the top level instructions, so don't descend + * into expressions. + */ +ir_visitor_status +discard_simplifier::visit_enter(ir_assignment *ir) +{ + return visit_continue_with_parent; +} + ir_visitor_status discard_simplifier::visit_enter(ir_if *ir) { diff --git a/mesalib/src/glsl/opt_if_simplification.cpp b/mesalib/src/glsl/opt_if_simplification.cpp index ce94f42fe..2d8858e49 100644 --- a/mesalib/src/glsl/opt_if_simplification.cpp +++ b/mesalib/src/glsl/opt_if_simplification.cpp @@ -38,10 +38,20 @@ public: } ir_visitor_status visit_leave(ir_if *); + ir_visitor_status visit_enter(ir_assignment *); bool made_progress; }; +/* We only care about the top level "if" instructions, so don't + * descend into expressions. + */ +ir_visitor_status +ir_if_simplification_visitor::visit_enter(ir_assignment *ir) +{ + return visit_continue_with_parent; +} + bool do_if_simplification(exec_list *instructions) { diff --git a/mesalib/src/glsl/opt_redundant_jumps.cpp b/mesalib/src/glsl/opt_redundant_jumps.cpp index edf4bb6c2..eb60519e2 100644 --- a/mesalib/src/glsl/opt_redundant_jumps.cpp +++ b/mesalib/src/glsl/opt_redundant_jumps.cpp @@ -1,111 +1,120 @@ -/* - * 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_redundant_jumps.cpp - * Remove certain types of redundant jumps - */ - -#include "ir.h" - -class redundant_jumps_visitor : public ir_hierarchical_visitor { -public: - redundant_jumps_visitor() - { - this->progress = false; - } - - virtual ir_visitor_status visit_leave(ir_if *); - virtual ir_visitor_status visit_leave(ir_loop *); - - bool progress; -}; - - -ir_visitor_status -redundant_jumps_visitor::visit_leave(ir_if *ir) -{ - /* If the last instruction in both branches is a 'break' or a 'continue', - * pull it out of the branches and insert it after the if-statment. Note - * that both must be the same type (either 'break' or 'continue'). - */ - ir_instruction *const last_then = - (ir_instruction *) ir->then_instructions.get_tail(); - ir_instruction *const last_else = - (ir_instruction *) ir->else_instructions.get_tail(); - - if ((last_then == NULL) || (last_else == NULL)) - return visit_continue; - - if ((last_then->ir_type != ir_type_loop_jump) - || (last_else->ir_type != ir_type_loop_jump)) - return visit_continue; - - ir_loop_jump *const then_jump = (ir_loop_jump *) last_then; - ir_loop_jump *const else_jump = (ir_loop_jump *) last_else; - - if (then_jump->mode != else_jump->mode) - return visit_continue; - - then_jump->remove(); - else_jump->remove(); - this->progress = true; - - ir->insert_after(then_jump); - - /* If both branchs of the if-statement are now empty, remove the - * if-statement. - */ - if (ir->then_instructions.is_empty() && ir->else_instructions.is_empty()) - ir->remove(); - - return visit_continue; -} - - -ir_visitor_status -redundant_jumps_visitor::visit_leave(ir_loop *ir) -{ - /* If the last instruction of a loop body is a 'continue', remove it. - */ - ir_instruction *const last = - (ir_instruction *) ir->body_instructions.get_tail(); - - if (last && (last->ir_type == ir_type_loop_jump) - && (((ir_loop_jump *) last)->mode == ir_loop_jump::jump_continue)) { - last->remove(); - this->progress = true; - } - - return visit_continue; -} - - -bool -optimize_redundant_jumps(exec_list *instructions) -{ - redundant_jumps_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_redundant_jumps.cpp + * Remove certain types of redundant jumps + */ + +#include "ir.h" + +class redundant_jumps_visitor : public ir_hierarchical_visitor { +public: + redundant_jumps_visitor() + { + this->progress = false; + } + + virtual ir_visitor_status visit_leave(ir_if *); + virtual ir_visitor_status visit_leave(ir_loop *); + virtual ir_visitor_status visit_enter(ir_assignment *); + + bool progress; +}; + +/* We only care about the top level instructions, so don't descend + * into expressions. + */ +ir_visitor_status +redundant_jumps_visitor::visit_enter(ir_assignment *ir) +{ + return visit_continue_with_parent; +} + +ir_visitor_status +redundant_jumps_visitor::visit_leave(ir_if *ir) +{ + /* If the last instruction in both branches is a 'break' or a 'continue', + * pull it out of the branches and insert it after the if-statment. Note + * that both must be the same type (either 'break' or 'continue'). + */ + ir_instruction *const last_then = + (ir_instruction *) ir->then_instructions.get_tail(); + ir_instruction *const last_else = + (ir_instruction *) ir->else_instructions.get_tail(); + + if ((last_then == NULL) || (last_else == NULL)) + return visit_continue; + + if ((last_then->ir_type != ir_type_loop_jump) + || (last_else->ir_type != ir_type_loop_jump)) + return visit_continue; + + ir_loop_jump *const then_jump = (ir_loop_jump *) last_then; + ir_loop_jump *const else_jump = (ir_loop_jump *) last_else; + + if (then_jump->mode != else_jump->mode) + return visit_continue; + + then_jump->remove(); + else_jump->remove(); + this->progress = true; + + ir->insert_after(then_jump); + + /* If both branchs of the if-statement are now empty, remove the + * if-statement. + */ + if (ir->then_instructions.is_empty() && ir->else_instructions.is_empty()) + ir->remove(); + + return visit_continue; +} + + +ir_visitor_status +redundant_jumps_visitor::visit_leave(ir_loop *ir) +{ + /* If the last instruction of a loop body is a 'continue', remove it. + */ + ir_instruction *const last = + (ir_instruction *) ir->body_instructions.get_tail(); + + if (last && (last->ir_type == ir_type_loop_jump) + && (((ir_loop_jump *) last)->mode == ir_loop_jump::jump_continue)) { + last->remove(); + this->progress = true; + } + + return visit_continue; +} + + +bool +optimize_redundant_jumps(exec_list *instructions) +{ + redundant_jumps_visitor v; + + v.run(instructions); + return v.progress; +} diff --git a/mesalib/src/glsl/opt_structure_splitting.cpp b/mesalib/src/glsl/opt_structure_splitting.cpp index 014407c0b..d08a5cee1 100644 --- a/mesalib/src/glsl/opt_structure_splitting.cpp +++ b/mesalib/src/glsl/opt_structure_splitting.cpp @@ -1,361 +1,367 @@ -/* - * 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_structure_splitting.cpp - * - * If a structure is only ever referenced by its components, then - * split those components out to individual variables so they can be - * handled normally by other optimization passes. - * - * This skips structures like uniforms, which need to be accessible as - * structures for their access by the GL. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_print_visitor.h" -#include "ir_rvalue_visitor.h" -#include "glsl_types.h" - -static bool debug = false; - -// XXX using variable_entry2 here to avoid collision (MSVC multiply-defined -// function) with the variable_entry class seen in ir_variable_refcount.h -// Perhaps we can use the one in ir_variable_refcount.h and make this class -// here go away? -class variable_entry2 : public exec_node -{ -public: - variable_entry2(ir_variable *var) - { - this->var = var; - this->whole_structure_access = 0; - this->declaration = false; - this->components = NULL; - this->mem_ctx = NULL; - } - - ir_variable *var; /* The key: the variable's pointer. */ - - /** Number of times the variable is referenced, including assignments. */ - unsigned whole_structure_access; - - bool declaration; /* If the variable had a decl in the instruction stream */ - - ir_variable **components; - - /** ralloc_parent(this->var) -- the shader's ralloc context. */ - void *mem_ctx; -}; - - -class ir_structure_reference_visitor : public ir_hierarchical_visitor { -public: - ir_structure_reference_visitor(void) - { - this->mem_ctx = ralloc_context(NULL); - this->variable_list.make_empty(); - } - - ~ir_structure_reference_visitor(void) - { - ralloc_free(mem_ctx); - } - - virtual ir_visitor_status visit(ir_variable *); - virtual ir_visitor_status visit(ir_dereference_variable *); - virtual ir_visitor_status visit_enter(ir_dereference_record *); - virtual ir_visitor_status visit_enter(ir_assignment *); - virtual ir_visitor_status visit_enter(ir_function_signature *); - - variable_entry2 *get_variable_entry2(ir_variable *var); - - /* List of variable_entry */ - exec_list variable_list; - - void *mem_ctx; -}; - -variable_entry2 * -ir_structure_reference_visitor::get_variable_entry2(ir_variable *var) -{ - assert(var); - - if (!var->type->is_record() || var->mode == ir_var_uniform) - return NULL; - - foreach_iter(exec_list_iterator, iter, this->variable_list) { - variable_entry2 *entry = (variable_entry2 *)iter.get(); - if (entry->var == var) - return entry; - } - - variable_entry2 *entry = new(mem_ctx) variable_entry2(var); - this->variable_list.push_tail(entry); - return entry; -} - - -ir_visitor_status -ir_structure_reference_visitor::visit(ir_variable *ir) -{ - variable_entry2 *entry = this->get_variable_entry2(ir); - - if (entry) - entry->declaration = true; - - return visit_continue; -} - -ir_visitor_status -ir_structure_reference_visitor::visit(ir_dereference_variable *ir) -{ - ir_variable *const var = ir->variable_referenced(); - variable_entry2 *entry = this->get_variable_entry2(var); - - if (entry) - entry->whole_structure_access++; - - return visit_continue; -} - -ir_visitor_status -ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir) -{ - (void) ir; - /* Don't descend into the ir_dereference_variable below. */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_structure_reference_visitor::visit_enter(ir_assignment *ir) -{ - if (ir->lhs->as_dereference_variable() && - ir->rhs->as_dereference_variable() && - !ir->condition) { - /* We'll split copies of a structure to copies of components, so don't - * descend to the ir_dereference_variables. - */ - return visit_continue_with_parent; - } - return visit_continue; -} - -ir_visitor_status -ir_structure_reference_visitor::visit_enter(ir_function_signature *ir) -{ - /* We don't want to descend into the function parameters and - * dead-code eliminate them, so just accept the body here. - */ - visit_list_elements(this, &ir->body); - return visit_continue_with_parent; -} - -class ir_structure_splitting_visitor : public ir_rvalue_visitor { -public: - ir_structure_splitting_visitor(exec_list *vars) - { - this->variable_list = vars; - } - - virtual ~ir_structure_splitting_visitor() - { - } - - virtual ir_visitor_status visit_leave(ir_assignment *); - - void split_deref(ir_dereference **deref); - void handle_rvalue(ir_rvalue **rvalue); - variable_entry2 *get_splitting_entry(ir_variable *var); - - exec_list *variable_list; - void *mem_ctx; -}; - -variable_entry2 * -ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var) -{ - assert(var); - - if (!var->type->is_record()) - return NULL; - - foreach_iter(exec_list_iterator, iter, *this->variable_list) { - variable_entry2 *entry = (variable_entry2 *)iter.get(); - if (entry->var == var) { - return entry; - } - } - - return NULL; -} - -void -ir_structure_splitting_visitor::split_deref(ir_dereference **deref) -{ - if ((*deref)->ir_type != ir_type_dereference_record) - return; - - ir_dereference_record *deref_record = (ir_dereference_record *)*deref; - ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable(); - if (!deref_var) - return; - - variable_entry2 *entry = get_splitting_entry(deref_var->var); - if (!entry) - return; - - unsigned int i; - for (i = 0; i < entry->var->type->length; i++) { - if (strcmp(deref_record->field, - entry->var->type->fields.structure[i].name) == 0) - break; - } - assert(i != entry->var->type->length); - - *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]); -} - -void -ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue) -{ - if (!*rvalue) - return; - - ir_dereference *deref = (*rvalue)->as_dereference(); - - if (!deref) - return; - - split_deref(&deref); - *rvalue = deref; -} - -ir_visitor_status -ir_structure_splitting_visitor::visit_leave(ir_assignment *ir) -{ - ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable(); - ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable(); - variable_entry2 *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL; - variable_entry2 *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL; - const glsl_type *type = ir->rhs->type; - - if ((lhs_entry || rhs_entry) && !ir->condition) { - for (unsigned int i = 0; i < type->length; i++) { - ir_dereference *new_lhs, *new_rhs; - void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx; - - if (lhs_entry) { - new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]); - } else { - new_lhs = new(mem_ctx) - ir_dereference_record(ir->lhs->clone(mem_ctx, NULL), - type->fields.structure[i].name); - } - - if (rhs_entry) { - new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]); - } else { - new_rhs = new(mem_ctx) - ir_dereference_record(ir->rhs->clone(mem_ctx, NULL), - type->fields.structure[i].name); - } - - ir->insert_before(new(mem_ctx) ir_assignment(new_lhs, - new_rhs, - NULL)); - } - ir->remove(); - } else { - handle_rvalue(&ir->rhs); - split_deref(&ir->lhs); - } - - handle_rvalue(&ir->condition); - - return visit_continue; -} - -bool -do_structure_splitting(exec_list *instructions) -{ - ir_structure_reference_visitor refs; - - visit_list_elements(&refs, instructions); - - /* Trim out variables we can't split. */ - foreach_iter(exec_list_iterator, iter, refs.variable_list) { - variable_entry2 *entry = (variable_entry2 *)iter.get(); - - if (debug) { - printf("structure %s@%p: decl %d, whole_access %d\n", - entry->var->name, (void *) entry->var, entry->declaration, - entry->whole_structure_access); - } - - if (!entry->declaration || entry->whole_structure_access) { - entry->remove(); - } - } - - if (refs.variable_list.is_empty()) - return false; - - void *mem_ctx = ralloc_context(NULL); - - /* Replace the decls of the structures to be split with their split - * components. - */ - foreach_iter(exec_list_iterator, iter, refs.variable_list) { - variable_entry2 *entry = (variable_entry2 *)iter.get(); - const struct glsl_type *type = entry->var->type; - - entry->mem_ctx = ralloc_parent(entry->var); - - entry->components = ralloc_array(mem_ctx, - ir_variable *, - type->length); - - for (unsigned int i = 0; i < entry->var->type->length; i++) { - const char *name = ralloc_asprintf(mem_ctx, "%s_%s", - entry->var->name, - type->fields.structure[i].name); - - entry->components[i] = - new(entry->mem_ctx) ir_variable(type->fields.structure[i].type, - name, - ir_var_temporary); - entry->var->insert_before(entry->components[i]); - } - - entry->var->remove(); - } - - ir_structure_splitting_visitor split(&refs.variable_list); - visit_list_elements(&split, instructions); - - ralloc_free(mem_ctx); - - return true; -} +/* + * 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_structure_splitting.cpp + * + * If a structure is only ever referenced by its components, then + * split those components out to individual variables so they can be + * handled normally by other optimization passes. + * + * This skips structures like uniforms, which need to be accessible as + * structures for their access by the GL. + */ + +#include "ir.h" +#include "ir_visitor.h" +#include "ir_print_visitor.h" +#include "ir_rvalue_visitor.h" +#include "glsl_types.h" + +static bool debug = false; + +// XXX using variable_entry2 here to avoid collision (MSVC multiply-defined +// function) with the variable_entry class seen in ir_variable_refcount.h +// Perhaps we can use the one in ir_variable_refcount.h and make this class +// here go away? +class variable_entry2 : public exec_node +{ +public: + variable_entry2(ir_variable *var) + { + this->var = var; + this->whole_structure_access = 0; + this->declaration = false; + this->components = NULL; + this->mem_ctx = NULL; + } + + ir_variable *var; /* The key: the variable's pointer. */ + + /** Number of times the variable is referenced, including assignments. */ + unsigned whole_structure_access; + + bool declaration; /* If the variable had a decl in the instruction stream */ + + ir_variable **components; + + /** ralloc_parent(this->var) -- the shader's ralloc context. */ + void *mem_ctx; +}; + + +class ir_structure_reference_visitor : public ir_hierarchical_visitor { +public: + ir_structure_reference_visitor(void) + { + this->mem_ctx = ralloc_context(NULL); + this->variable_list.make_empty(); + } + + ~ir_structure_reference_visitor(void) + { + ralloc_free(mem_ctx); + } + + virtual ir_visitor_status visit(ir_variable *); + virtual ir_visitor_status visit(ir_dereference_variable *); + virtual ir_visitor_status visit_enter(ir_dereference_record *); + virtual ir_visitor_status visit_enter(ir_assignment *); + virtual ir_visitor_status visit_enter(ir_function_signature *); + + variable_entry2 *get_variable_entry2(ir_variable *var); + + /* List of variable_entry */ + exec_list variable_list; + + void *mem_ctx; +}; + +variable_entry2 * +ir_structure_reference_visitor::get_variable_entry2(ir_variable *var) +{ + assert(var); + + if (!var->type->is_record() || var->mode == ir_var_uniform) + return NULL; + + foreach_iter(exec_list_iterator, iter, this->variable_list) { + variable_entry2 *entry = (variable_entry2 *)iter.get(); + if (entry->var == var) + return entry; + } + + variable_entry2 *entry = new(mem_ctx) variable_entry2(var); + this->variable_list.push_tail(entry); + return entry; +} + + +ir_visitor_status +ir_structure_reference_visitor::visit(ir_variable *ir) +{ + variable_entry2 *entry = this->get_variable_entry2(ir); + + if (entry) + entry->declaration = true; + + return visit_continue; +} + +ir_visitor_status +ir_structure_reference_visitor::visit(ir_dereference_variable *ir) +{ + ir_variable *const var = ir->variable_referenced(); + variable_entry2 *entry = this->get_variable_entry2(var); + + if (entry) + entry->whole_structure_access++; + + return visit_continue; +} + +ir_visitor_status +ir_structure_reference_visitor::visit_enter(ir_dereference_record *ir) +{ + (void) ir; + /* Don't descend into the ir_dereference_variable below. */ + return visit_continue_with_parent; +} + +ir_visitor_status +ir_structure_reference_visitor::visit_enter(ir_assignment *ir) +{ + /* If there are no structure references yet, no need to bother with + * processing the expression tree. + */ + if (this->variable_list.is_empty()) + return visit_continue_with_parent; + + if (ir->lhs->as_dereference_variable() && + ir->rhs->as_dereference_variable() && + !ir->condition) { + /* We'll split copies of a structure to copies of components, so don't + * descend to the ir_dereference_variables. + */ + return visit_continue_with_parent; + } + return visit_continue; +} + +ir_visitor_status +ir_structure_reference_visitor::visit_enter(ir_function_signature *ir) +{ + /* We don't want to descend into the function parameters and + * dead-code eliminate them, so just accept the body here. + */ + visit_list_elements(this, &ir->body); + return visit_continue_with_parent; +} + +class ir_structure_splitting_visitor : public ir_rvalue_visitor { +public: + ir_structure_splitting_visitor(exec_list *vars) + { + this->variable_list = vars; + } + + virtual ~ir_structure_splitting_visitor() + { + } + + virtual ir_visitor_status visit_leave(ir_assignment *); + + void split_deref(ir_dereference **deref); + void handle_rvalue(ir_rvalue **rvalue); + variable_entry2 *get_splitting_entry(ir_variable *var); + + exec_list *variable_list; + void *mem_ctx; +}; + +variable_entry2 * +ir_structure_splitting_visitor::get_splitting_entry(ir_variable *var) +{ + assert(var); + + if (!var->type->is_record()) + return NULL; + + foreach_iter(exec_list_iterator, iter, *this->variable_list) { + variable_entry2 *entry = (variable_entry2 *)iter.get(); + if (entry->var == var) { + return entry; + } + } + + return NULL; +} + +void +ir_structure_splitting_visitor::split_deref(ir_dereference **deref) +{ + if ((*deref)->ir_type != ir_type_dereference_record) + return; + + ir_dereference_record *deref_record = (ir_dereference_record *)*deref; + ir_dereference_variable *deref_var = deref_record->record->as_dereference_variable(); + if (!deref_var) + return; + + variable_entry2 *entry = get_splitting_entry(deref_var->var); + if (!entry) + return; + + unsigned int i; + for (i = 0; i < entry->var->type->length; i++) { + if (strcmp(deref_record->field, + entry->var->type->fields.structure[i].name) == 0) + break; + } + assert(i != entry->var->type->length); + + *deref = new(entry->mem_ctx) ir_dereference_variable(entry->components[i]); +} + +void +ir_structure_splitting_visitor::handle_rvalue(ir_rvalue **rvalue) +{ + if (!*rvalue) + return; + + ir_dereference *deref = (*rvalue)->as_dereference(); + + if (!deref) + return; + + split_deref(&deref); + *rvalue = deref; +} + +ir_visitor_status +ir_structure_splitting_visitor::visit_leave(ir_assignment *ir) +{ + ir_dereference_variable *lhs_deref = ir->lhs->as_dereference_variable(); + ir_dereference_variable *rhs_deref = ir->rhs->as_dereference_variable(); + variable_entry2 *lhs_entry = lhs_deref ? get_splitting_entry(lhs_deref->var) : NULL; + variable_entry2 *rhs_entry = rhs_deref ? get_splitting_entry(rhs_deref->var) : NULL; + const glsl_type *type = ir->rhs->type; + + if ((lhs_entry || rhs_entry) && !ir->condition) { + for (unsigned int i = 0; i < type->length; i++) { + ir_dereference *new_lhs, *new_rhs; + void *mem_ctx = lhs_entry ? lhs_entry->mem_ctx : rhs_entry->mem_ctx; + + if (lhs_entry) { + new_lhs = new(mem_ctx) ir_dereference_variable(lhs_entry->components[i]); + } else { + new_lhs = new(mem_ctx) + ir_dereference_record(ir->lhs->clone(mem_ctx, NULL), + type->fields.structure[i].name); + } + + if (rhs_entry) { + new_rhs = new(mem_ctx) ir_dereference_variable(rhs_entry->components[i]); + } else { + new_rhs = new(mem_ctx) + ir_dereference_record(ir->rhs->clone(mem_ctx, NULL), + type->fields.structure[i].name); + } + + ir->insert_before(new(mem_ctx) ir_assignment(new_lhs, + new_rhs, + NULL)); + } + ir->remove(); + } else { + handle_rvalue(&ir->rhs); + split_deref(&ir->lhs); + } + + handle_rvalue(&ir->condition); + + return visit_continue; +} + +bool +do_structure_splitting(exec_list *instructions) +{ + ir_structure_reference_visitor refs; + + visit_list_elements(&refs, instructions); + + /* Trim out variables we can't split. */ + foreach_iter(exec_list_iterator, iter, refs.variable_list) { + variable_entry2 *entry = (variable_entry2 *)iter.get(); + + if (debug) { + printf("structure %s@%p: decl %d, whole_access %d\n", + entry->var->name, (void *) entry->var, entry->declaration, + entry->whole_structure_access); + } + + if (!entry->declaration || entry->whole_structure_access) { + entry->remove(); + } + } + + if (refs.variable_list.is_empty()) + return false; + + void *mem_ctx = ralloc_context(NULL); + + /* Replace the decls of the structures to be split with their split + * components. + */ + foreach_iter(exec_list_iterator, iter, refs.variable_list) { + variable_entry2 *entry = (variable_entry2 *)iter.get(); + const struct glsl_type *type = entry->var->type; + + entry->mem_ctx = ralloc_parent(entry->var); + + entry->components = ralloc_array(mem_ctx, + ir_variable *, + type->length); + + for (unsigned int i = 0; i < entry->var->type->length; i++) { + const char *name = ralloc_asprintf(mem_ctx, "%s_%s", + entry->var->name, + type->fields.structure[i].name); + + entry->components[i] = + new(entry->mem_ctx) ir_variable(type->fields.structure[i].type, + name, + ir_var_temporary); + entry->var->insert_before(entry->components[i]); + } + + entry->var->remove(); + } + + ir_structure_splitting_visitor split(&refs.variable_list); + visit_list_elements(&split, instructions); + + ralloc_free(mem_ctx); + + return true; +} -- cgit v1.2.3