/* * Mesa 3-D graphics library * Version: 6.5 * * Copyright (C) 2005-2006 Brian Paul All Rights Reserved. * * 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 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 * BRIAN PAUL 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 slang_assemble_typeinfo.c * slang type info * \author Michal Krol */ #include "imports.h" #include "slang_typeinfo.h" #include "slang_compile.h" #include "slang_error.h" #include "prog_instruction.h" /** * Checks if a field selector is a general swizzle (an r-value swizzle * with replicated components or an l-value swizzle mask) for a * vector. Returns GL_TRUE if this is the case, is filled with * swizzle information. Returns GL_FALSE otherwise. */ GLboolean _slang_is_swizzle(const char *field, GLuint rows, slang_swizzle * swz) { GLuint i; GLboolean xyzw = GL_FALSE, rgba = GL_FALSE, stpq = GL_FALSE; /* init to undefined. * We rely on undefined/nil values to distinguish between * regular swizzles and writemasks. * For example, the swizzle ".xNNN" is the writemask ".x". * That's different than the swizzle ".xxxx". */ for (i = 0; i < 4; i++) swz->swizzle[i] = SWIZZLE_NIL; /* the swizzle can be at most 4-component long */ swz->num_components = slang_string_length(field); if (swz->num_components > 4) return GL_FALSE; for (i = 0; i < swz->num_components; i++) { /* mark which swizzle group is used */ switch (field[i]) { case 'x': case 'y': case 'z': case 'w': xyzw = GL_TRUE; break; case 'r': case 'g': case 'b': case 'a': rgba = GL_TRUE; break; case 's': case 't': case 'p': case 'q': stpq = GL_TRUE; break; default: return GL_FALSE; } /* collect swizzle component */ switch (field[i]) { case 'x': case 'r': case 's': swz->swizzle[i] = 0; break; case 'y': case 'g': case 't': swz->swizzle[i] = 1; break; case 'z': case 'b': case 'p': swz->swizzle[i] = 2; break; case 'w': case 'a': case 'q': swz->swizzle[i] = 3; break; } /* check if the component is valid for given vector's row count */ if (rows <= swz->swizzle[i]) return GL_FALSE; } /* only one swizzle group can be used */ if ((xyzw && rgba) || (xyzw && stpq) || (rgba && stpq)) return GL_FALSE; return GL_TRUE; } /** * Checks if a general swizzle is an l-value swizzle - these swizzles * do not have duplicated fields. Returns GL_TRUE if this is a * swizzle mask. Returns GL_FALSE otherwise */ GLboolean _slang_is_swizzle_mask(const slang_swizzle * swz, GLuint rows) { GLuint i, c = 0; /* the swizzle may not be longer than the vector dim */ if (swz->num_components > rows) return GL_FALSE; /* the swizzle components cannot be duplicated */ for (i = 0; i < swz->num_components; i++) { if ((c & (1 << swz->swizzle[i])) != 0) return GL_FALSE; c |= 1 << swz->swizzle[i]; } return GL_TRUE; } /** * Combines (multiplies) two swizzles to form single swizzle. * Example: "vec.wzyx.yx" --> "vec.zw". */ GLvoid _slang_multiply_swizzles(slang_swizzle * dst, const slang_swizzle * left, const slang_swizzle * right) { GLuint i; dst->num_components = right->num_components; for (i = 0; i < right->num_components; i++) dst->swizzle[i] = left->swizzle[right->swizzle[i]]; } GLvoid slang_type_specifier_ctr(slang_type_specifier * self) { self->type = slang_spec_void; self->_struct = NULL; self->_array = NULL; } GLvoid slang_type_specifier_dtr(slang_type_specifier * self) { if (self->_struct != NULL) { slang_struct_destruct(self->_struct); slang_alloc_free(self->_struct); } if (self->_array != NULL) { slang_type_specifier_dtr(self->_array); slang_alloc_free(self->_array); } } GLboolean slang_type_specifier_copy(slang_type_specifier * x, const slang_type_specifier * y) { slang_type_specifier z; slang_type_specifier_ctr(&z); z.type = y->type; if (z.type == slang_spec_struct) { z._struct = (slang_struct *) slang_alloc_malloc(sizeof(slang_struct)); if (z._struct == NULL) { slang_type_specifier_dtr(&z); return GL_FALSE; } if (!slang_struct_construct(z._struct)) { slang_alloc_free(z._struct); slang_type_specifier_dtr(&z); return GL_FALSE; } if (!slang_struct_copy(z._struct, y->_struct)) { slang_type_specifier_dtr(&z); return GL_FALSE; } } else if (z.type == slang_spec_array) { z._array = (slang_type_specifier *) slang_alloc_malloc(sizeof(slang_type_specifier)); if (z._array == NULL) { slang_type_specifier_dtr(&z); return GL_FALSE; } slang_type_specifier_ctr(z._array); if (!slang_type_specifier_copy(z._array, y->_array)) { slang_type_specifier_dtr(&z); return GL_FALSE; } } slang_type_specifier_dtr(x); *x = z; return GL_TRUE; } GLboolean slang_type_specifier_equal(const slang_type_specifier * x, const slang_type_specifier * y) { if (x->type != y->type) return 0; if (x->type == slang_spec_struct) return slang_struct_equal(x->_struct, y->_struct); if (x->type == slang_spec_array) return slang_type_specifier_equal(x->_array, y->_array); return 1; } GLboolean slang_typeinfo_construct(slang_typeinfo * ti) { slang_type_specifier_ctr(&ti->spec); ti->array_len = 0; return GL_TRUE; } GLvoid slang_typeinfo_destruct(slang_typeinfo * ti) { slang_type_specifier_dtr(&ti->spec); } /** * Determine the return type of a function. * \param name name of the function * \param params array of function parameters * \param num_params number of parameters * \param space namespace to use * \param spec returns the function's type * \param atoms atom pool * \return GL_TRUE for success, GL_FALSE if failure */ static GLboolean typeof_existing_function(const char *name, const slang_operation * params, GLuint num_params, const slang_name_space * space, slang_type_specifier * spec, slang_atom_pool * atoms) { slang_atom atom; GLboolean exists; atom = slang_atom_pool_atom(atoms, name); if (!_slang_typeof_function(atom, params, num_params, space, spec, &exists, atoms)) return GL_FALSE; return exists; } GLboolean _slang_typeof_operation(const slang_assemble_ctx * A, const slang_operation * op, slang_typeinfo * ti) { return _slang_typeof_operation_(op, &A->space, ti, A->atoms); } /** * Determine the return type of an operation. * \param op the operation node * \param space the namespace to use * \param ti the returned type * \param atoms atom pool * \return GL_TRUE for success, GL_FALSE if failure */ GLboolean _slang_typeof_operation_(const slang_operation * op, const slang_name_space * space, slang_typeinfo * ti, slang_atom_pool * atoms) { ti->can_be_referenced = GL_FALSE; ti->is_swizzled = GL_FALSE; switch (op->type) { case slang_oper_block_no_new_scope: case slang_oper_block_new_scope: case slang_oper_variable_decl: case slang_oper_asm: case slang_oper_break: case slang_oper_continue: case slang_oper_discard: case slang_oper_return: case slang_oper_if: case slang_oper_while: case slang_oper_do: case slang_oper_for: case slang_oper_void: ti->spec.type = slang_spec_void; break; case slang_oper_expression: case slang_oper_assign: case slang_oper_addassign: case slang_oper_subassign: case slang_oper_mulassign: case slang_oper_divassign: case slang_oper_preincrement: case slang_oper_predecrement: if (!_slang_typeof_operation_(op->children, space, ti, atoms)) return GL_FALSE; break; case slang_oper_literal_bool: if (op->literal_size == 1) ti->spec.type = slang_spec_bool; else if (op->literal_size == 2) ti->spec.type = slang_spec_bvec2; else if (op->literal_size == 3) ti->spec.type = slang_spec_bvec3; else if (op->literal_size == 4) ti->spec.type = slang_spec_bvec4; else { _mesa_problem(NULL, "Unexpected bool literal_size %d in _slang_typeof_operation()", op->literal_size); ti->spec.type = slang_spec_bool; } break; case slang_oper_logicalor: case slang_oper_logicalxor: case slang_oper_logicaland: case slang_oper_equal: case slang_oper_notequal: case slang_oper_less: case slang_oper_greater: case slang_oper_lessequal: case slang_oper_greaterequal: case slang_oper_not: ti->spec.type = slang_spec_bool; break; case slang_oper_literal_int: if (op->literal_size == 1) ti->spec.type = slang_spec_int; else if (op->literal_size == 2) ti->spec.type = slang_spec_ivec2; else if (op->literal_size == 3) ti->spec.type = slang_spec_ivec3; else if (op->literal_size == 4) ti->spec.type = slang_spec_ivec4; else { _mesa_problem(NULL, "Unexpected int literal_size %d in _slang_typeof_operation()", op->literal_size); ti->spec.type = slang_spec_int; } break; case slang_oper_literal_float: if (op->literal_size == 1) ti->spec.type = slang_spec_float; else if (op->literal_size == 2) ti->spec.type = slang_spec_vec2; else if (op->literal_size == 3) ti->spec.type = slang_spec_vec3; else if (op->literal_size == 4) ti->spec.type = slang_spec_vec4; else { _mesa_problem(NULL, "Unexpected float literal_size %d in _slang_typeof_operation()", op->literal_size); ti->spec.type = slang_spec_float; } break; case slang_oper_identifier: { slang_variable *var; var = _slang_locate_variable(op->locals, op->a_id, GL_TRUE); if (var == NULL) RETURN_ERROR2("undefined variable", (char *) op->a_id, 0); if (!slang_type_specifier_copy(&ti->spec, &var->type.specifier)) RETURN_OUT_OF_MEMORY(); ti->can_be_referenced = GL_TRUE; ti->array_len = var->array_len; } break; case slang_oper_sequence: /* TODO: check [0] and [1] if they match */ if (!_slang_typeof_operation_(&op->children[1], space, ti, atoms)) RETURN_NIL(); ti->can_be_referenced = GL_FALSE; ti->is_swizzled = GL_FALSE; break; /*case slang_oper_modassign: */ /*case slang_oper_lshassign: */ /*case slang_oper_rshassign: */ /*case slang_oper_orassign: */ /*case slang_oper_xorassign: */ /*case slang_oper_andassign: */ case slang_oper_select: /* TODO: check [1] and [2] if they match */ if (!_slang_typeof_operation_(&op->children[1], space, ti, atoms)) RETURN_NIL(); ti->can_be_referenced = GL_FALSE; ti->is_swizzled = GL_FALSE; break; /*case slang_oper_bitor: */ /*case slang_oper_bitxor: */ /*case slang_oper_bitand: */ /*case slang_oper_lshift: */ /*case slang_oper_rshift: */ case slang_oper_add: if (!typeof_existing_function("+", op->children, 2, space, &ti->spec, atoms)) RETURN_NIL(); break; case slang_oper_subtract: if (!typeof_existing_function("-", op->children, 2, space, &ti->spec, atoms)) RETURN_NIL(); break; case slang_oper_multiply: if (!typeof_existing_function("*", op->children, 2, space, &ti->spec, atoms)) RETURN_NIL(); break; case slang_oper_divide: if (!typeof_existing_function("/", op->children, 2, space, &ti->spec, atoms)) RETURN_NIL(); break; /*case slang_oper_modulus: */ case slang_oper_plus: if (!_slang_typeof_operation_(op->children, space, ti, atoms)) RETURN_NIL(); ti->can_be_referenced = GL_FALSE; ti->is_swizzled = GL_FALSE; break; case slang_oper_minus: if (!typeof_existing_function("-", op->children, 1, space, &ti->spec, atoms)) RETURN_NIL(); break; /*case slang_oper_complement: */ case slang_oper_subscript: { slang_typeinfo _ti; if (!slang_typeinfo_construct(&_ti)) RETURN_NIL(); if (!_slang_typeof_operation_(op->children, space, &_ti, atoms)) { slang_typeinfo_destruct(&_ti); RETURN_NIL(); } ti->can_be_referenced = _ti.can_be_referenced; if (_ti.spec.type == slang_spec_array) { if (!slang_type_specifier_copy(&ti->spec, _ti.spec._array)) { slang_typeinfo_destruct(&_ti); RETURN_NIL(); } } else { if (!_slang_type_is_vector(_ti.spec.type) && !_slang_type_is_matrix(_ti.spec.type)) { slang_typeinfo_destruct(&_ti); RETURN_ERROR("cannot index a non-array type", 0); } ti->spec.type = _slang_type_base(_ti.spec.type); } slang_typeinfo_destruct(&_ti); } break; case slang_oper_call: { GLboolean exists; if (!_slang_typeof_function(op->a_id, op->children, op->num_children, space, &ti->spec, &exists, atoms)) RETURN_NIL(); if (!exists) { slang_struct *s = slang_struct_scope_find(space->structs, op->a_id, GL_TRUE); if (s != NULL) { ti->spec.type = slang_spec_struct; ti->spec._struct = (slang_struct *) slang_alloc_malloc(sizeof(slang_struct)); if (ti->spec._struct == NULL) RETURN_NIL(); if (!slang_struct_construct(ti->spec._struct)) { slang_alloc_free(ti->spec._struct); ti->spec._struct = NULL; RETURN_NIL(); } if (!slang_struct_copy(ti->spec._struct, s)) RETURN_NIL(); } else { const char *name; slang_type_specifier_type type; name = slang_atom_pool_id(atoms, op->a_id); type = slang_type_specifier_type_from_string(name); if (type == slang_spec_void) RETURN_ERROR2("function not found", name, 0); ti->spec.type = type; } } } break; case slang_oper_field: { slang_typeinfo _ti; if (!slang_typeinfo_construct(&_ti)) RETURN_NIL(); if (!_slang_typeof_operation_(op->children, space, &_ti, atoms)) { slang_typeinfo_destruct(&_ti); RETURN_NIL(); } if (_ti.spec.type == slang_spec_struct) { slang_variable *field; field = _slang_locate_variable(_ti.spec._struct->fields, op->a_id, GL_FALSE); if (field == NULL) { slang_typeinfo_destruct(&_ti); RETURN_NIL(); } if (!slang_type_specifier_copy(&ti->spec, &field->type.specifier)) { slang_typeinfo_destruct(&_ti); RETURN_NIL(); } ti->can_be_referenced = _ti.can_be_referenced; } else { GLuint rows; const char *swizzle; slang_type_specifier_type base; /* determine the swizzle of the field expression */ #if 000 if (!_slang_type_is_vector(_ti.spec.type)) { slang_typeinfo_destruct(&_ti); RETURN_ERROR("Can't swizzle scalar expression", 0); } #endif rows = _slang_type_dim(_ti.spec.type); swizzle = slang_atom_pool_id(atoms, op->a_id); if (!_slang_is_swizzle(swizzle, rows, &ti->swz)) { slang_typeinfo_destruct(&_ti); RETURN_ERROR("Bad swizzle", 0); } ti->is_swizzled = GL_TRUE; ti->can_be_referenced = _ti.can_be_referenced && _slang_is_swizzle_mask(&ti->swz, rows); if (_ti.is_swizzled) { slang_swizzle swz; /* swizzle the swizzle */ _slang_multiply_swizzles(&swz, &_ti.swz, &ti->swz); ti->swz = swz; } base = _slang_type_base(_ti.spec.type); switch (ti->swz.num_components) { case 1: ti->spec.type = base; break; case 2: switch (base) { case slang_spec_float: ti->spec.type = slang_spec_vec2; break; case slang_spec_int: ti->spec.type = slang_spec_ivec2; break; case slang_spec_bool: ti->spec.type = slang_spec_bvec2; break; default: break; } break; case 3: switch (base) { case slang_spec_float: ti->spec.type = slang_spec_vec3; break; case slang_spec_int: ti->spec.type = slang_spec_ivec3; break; case slang_spec_bool: ti->spec.type = slang_spec_bvec3; break; default: break; } break; case 4: switch (base) { case slang_spec_float: ti->spec.type = slang_spec_vec4; break; case slang_spec_int: ti->spec.type = slang_spec_ivec4; break; case slang_spec_bool: ti->spec.type = slang_spec_bvec4; break; default: break; } break; default: break; } } slang_typeinfo_destruct(&_ti); } break; case slang_oper_postincrement: case slang_oper_postdecrement: if (!_slang_typeof_operation_(op->children, space, ti, atoms)) RETURN_NIL(); ti->can_be_referenced = GL_FALSE; ti->is_swizzled = GL_FALSE; break; default: RETURN_NIL(); } return GL_TRUE; } slang_function * _slang_locate_function(const slang_function_scope * funcs, slang_atom a_name, const slang_operation * args, GLuint num_args, const slang_name_space * space, slang_atom_pool * atoms) { GLuint i; for (i = 0; i < funcs->num_functions; i++) { slang_function *f = &funcs->functions[i]; const GLuint haveRetValue = _slang_function_has_return_value(f); GLuint j; if (a_name != f->header.a_name) continue; if (f->param_count - haveRetValue != num_args) continue; /* compare parameter / argument types */ for (j = 0; j < num_args; j++) { slang_typeinfo ti; if (!slang_typeinfo_construct(&ti)) return NULL; if (!_slang_typeof_operation_(&args[j], space, &ti, atoms)) { slang_typeinfo_destruct(&ti); return NULL; } if (!slang_type_specifier_equal(&ti.spec, &f->parameters->variables[j]->type.specifier)) { slang_typeinfo_destruct(&ti); break; } slang_typeinfo_destruct(&ti); /* "out" and "inout" formal parameter requires the actual parameter to be l-value */ if (!ti.can_be_referenced && (f->parameters->variables[j]->type.qualifier == slang_qual_out || f->parameters->variables[j]->type.qualifier == slang_qual_inout)) break; } if (j == num_args) return f; } if (funcs->outer_scope != NULL) return _slang_locate_function(funcs->outer_scope, a_name, args, num_args, space, atoms); return NULL; } /** * Determine the return type of a function. * \param a_name the function name * \param param function parameters (overloading) * \param num_params number of parameters to function * \param space namespace to search * \param exists returns GL_TRUE or GL_FALSE to indicate existance of function * \return GL_TRUE for success, GL_FALSE if failure (bad function name) */ GLboolean _slang_typeof_function(slang_atom a_name, const slang_operation * params, GLuint num_params, const slang_name_space * space, slang_type_specifier * spec, GLboolean * exists, slang_atom_pool * atoms) { slang_function *fun = _slang_locate_function(space->funcs, a_name, params, num_params, space, atoms); *exists = fun != NULL; if (!fun) return GL_TRUE; /* yes, not false */ return slang_type_specifier_copy(spec, &fun->header.type.specifier); } /** * Determine if a type is a matrix. * \return GL_TRUE if is a matrix, GL_FALSE otherwise. */ GLboolean _slang_type_is_matrix(slang_type_specifier_type ty) { switch (ty) { case slang_spec_mat2: case slang_spec_mat3: case slang_spec_mat4: return GL_TRUE; default: return GL_FALSE; } } /** * Determine if a type is a vector. * \return GL_TRUE if is a vector, GL_FALSE otherwise. */ GLboolean _slang_type_is_vector(slang_type_specifier_type ty) { switch (ty) { case slang_spec_vec2: case slang_spec_vec3: case slang_spec_vec4: case slang_spec_ivec2: case slang_spec_ivec3: case slang_spec_ivec4: case slang_spec_bvec2: case slang_spec_bvec3: case slang_spec_bvec4: return GL_TRUE; default: return GL_FALSE; } } /** * Given a vector type, return the type of the vector's elements */ slang_type_specifier_type _slang_type_base(slang_type_specifier_type ty) { switch (ty) { case slang_spec_float: case slang_spec_vec2: case slang_spec_vec3: case slang_spec_vec4: return slang_spec_float; case slang_spec_int: case slang_spec_ivec2: case slang_spec_ivec3: case slang_spec_ivec4: return slang_spec_int; case slang_spec_bool: case slang_spec_bvec2: case slang_spec_bvec3: case slang_spec_bvec4: return slang_spec_bool; case slang_spec_mat2: return slang_spec_vec2; case slang_spec_mat3: return slang_spec_vec3; case slang_spec_mat4: return slang_spec_vec4; default: return slang_spec_void; } } /** * Return the dimensionality of a vector or matrix type. */ GLuint _slang_type_dim(slang_type_specifier_type ty) { switch (ty) { case slang_spec_float: case slang_spec_int: case slang_spec_bool: return 1; case slang_spec_vec2: case slang_spec_ivec2: case slang_spec_bvec2: case slang_spec_mat2: return 2; case slang_spec_vec3: case slang_spec_ivec3: case slang_spec_bvec3: case slang_spec_mat3: return 3; case slang_spec_vec4: case slang_spec_ivec4: case slang_spec_bvec4: case slang_spec_mat4: return 4; default: return 0; } }