diff options
Diffstat (limited to 'mesalib/src/glsl')
32 files changed, 14987 insertions, 14370 deletions
diff --git a/mesalib/src/glsl/Makefile b/mesalib/src/glsl/Makefile index e4b992dbc..b63307f53 100644 --- a/mesalib/src/glsl/Makefile +++ b/mesalib/src/glsl/Makefile @@ -1,213 +1,213 @@ - -#src/glsl/pp/Makefile - -TOP = ../.. - -include $(TOP)/configs/current - -LIBNAME = glsl - -LIBGLCPP_SOURCES = \ - glcpp/glcpp-lex.c \ - glcpp/glcpp-parse.c \ - glcpp/pp.c - -GLCPP_SOURCES = \ - $(LIBGLCPP_SOURCES) \ - ralloc.c \ - glcpp/glcpp.c - -C_SOURCES = \ - strtod.c \ - ralloc.c \ - $(LIBGLCPP_SOURCES) - -CXX_SOURCES = \ - ast_expr.cpp \ - ast_function.cpp \ - ast_to_hir.cpp \ - ast_type.cpp \ - glsl_lexer.cpp \ - glsl_parser.cpp \ - glsl_parser_extras.cpp \ - glsl_types.cpp \ - glsl_symbol_table.cpp \ - hir_field_selection.cpp \ - ir_basic_block.cpp \ - ir_clone.cpp \ - ir_constant_expression.cpp \ - ir.cpp \ - ir_expression_flattening.cpp \ - ir_function_can_inline.cpp \ - ir_function.cpp \ - ir_hierarchical_visitor.cpp \ - ir_hv_accept.cpp \ - ir_import_prototypes.cpp \ - ir_print_visitor.cpp \ - ir_reader.cpp \ - ir_rvalue_visitor.cpp \ - ir_set_program_inouts.cpp \ - ir_validate.cpp \ - ir_variable.cpp \ - ir_variable_refcount.cpp \ - linker.cpp \ - link_functions.cpp \ - loop_analysis.cpp \ - loop_controls.cpp \ - loop_unroll.cpp \ - lower_discard.cpp \ - lower_if_to_cond_assign.cpp \ - lower_instructions.cpp \ - lower_jumps.cpp \ - lower_mat_op_to_vec.cpp \ - lower_noise.cpp \ - lower_texture_projection.cpp \ - lower_variable_index_to_cond_assign.cpp \ - lower_vec_index_to_cond_assign.cpp \ - lower_vec_index_to_swizzle.cpp \ - lower_vector.cpp \ - opt_algebraic.cpp \ - opt_constant_folding.cpp \ - opt_constant_propagation.cpp \ - opt_constant_variable.cpp \ - opt_copy_propagation.cpp \ - opt_copy_propagation_elements.cpp \ - opt_dead_code.cpp \ - opt_dead_code_local.cpp \ - opt_dead_functions.cpp \ - opt_discard_simplification.cpp \ - opt_function_inlining.cpp \ - opt_if_simplification.cpp \ - opt_noop_swizzle.cpp \ - opt_redundant_jumps.cpp \ - opt_structure_splitting.cpp \ - opt_swizzle_swizzle.cpp \ - opt_tree_grafting.cpp \ - s_expression.cpp - -LIBS = \ - $(TOP)/src/glsl/libglsl.a - -APPS = glsl_compiler glcpp/glcpp - -GLSL2_C_SOURCES = \ - ../mesa/program/hash_table.c \ - ../mesa/program/symbol_table.c -GLSL2_CXX_SOURCES = \ - main.cpp - -GLSL2_OBJECTS = \ - $(GLSL2_C_SOURCES:.c=.o) \ - $(GLSL2_CXX_SOURCES:.cpp=.o) - -### Basic defines ### - -DEFINES += \ - $(LIBRARY_DEFINES) \ - $(API_DEFINES) - -GLCPP_OBJECTS = \ - $(GLCPP_SOURCES:.c=.o) \ - ../mesa/program/hash_table.o - -OBJECTS = \ - $(C_SOURCES:.c=.o) \ - $(CXX_SOURCES:.cpp=.o) - -DRICORE_OBJ_DIR = obj-visible -OBJECTS_DRICORE = $(addprefix $(DRICORE_OBJ_DIR)/,$(OBJECTS)) - -INCLUDES = \ - -I. \ - -I../mesa \ - -I../mapi \ - -I../../include \ - $(LIBRARY_INCLUDES) - -ALL_SOURCES = \ - $(C_SOURCES) \ - $(CXX_SOURCES) \ - $(GLSL2_CXX_SOURCES) \ - $(GLSL2_C_SOURCES) - -##### TARGETS ##### - -default: depend lib$(LIBNAME).a $(APPS) $(DRICORE_GLSL_LIBS) - -$(TOP)/$(LIB_DIR)/libglsl.so: $(OBJECTS_DRICORE) builtin_function.o Makefile $(TOP)/src/glsl/Makefile.template - $(MKLIB) -o $@ -linker '$(CXX)' -ldflags '$(LDFLAGS)' \ - -cplusplus -noprefix \ - -install $(TOP)/$(LIB_DIR) -id $(INSTALL_LIB_DIR)/$@.dylib \ - $(OBJECTS_DRICORE) builtin_function.o - -lib$(LIBNAME).a: $(OBJECTS) builtin_function.o Makefile $(TOP)/src/glsl/Makefile.template - $(MKLIB) -cplusplus -o $(LIBNAME) -static $(OBJECTS) builtin_function.o - -depend: $(ALL_SOURCES) Makefile - rm -f depend - touch depend - $(MKDEP) $(MKDEP_OPTIONS) $(INCLUDES) $(ALL_SOURCES) 2> /dev/null - -# Remove .o and backup files -clean: clean-dricore - rm -f $(GLCPP_OBJECTS) $(GLSL2_OBJECTS) $(OBJECTS) lib$(LIBNAME).a depend depend.bak builtin_function.cpp builtin_function.o builtin_stubs.o builtin_compiler - -rm -f $(APPS) - -clean-dricore: - -rm -f $(DRICORE_OBJ_DIR) $(TOP)/$(LIB_DIR)/libglsl.so libglsl.so - -ifneq (,$(DRICORE_GLSL_LIBS)) -DRICORE_INSTALL_TARGET = install-dricore -endif - -# Dummy target -install: $(DRICORE_INSTALL_TARGET) - @echo -n "" - -install-dricore: default - $(INSTALL) -d $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR) - $(INSTALL) -m 755 $(DRICORE_GLSL_LIBS) $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR) - -##### RULES ##### - -glsl_compiler: $(GLSL2_OBJECTS) libglsl.a builtin_stubs.o - $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) builtin_stubs.o $(LIBS) -o $@ - -glcpp: glcpp/glcpp -glcpp/glcpp: $(GLCPP_OBJECTS) - $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) -o $@ - -.cpp.o: - $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@ - -.c.o: - $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@ - -$(DRICORE_OBJ_DIR)/%.o : %.cpp - @mkdir -p $(dir $@) - $(CXX) -c $(INCLUDES) $(DRI_CXXFLAGS) $(DEFINES) $< -o $@ - -$(DRICORE_OBJ_DIR)/%.o : %.c - @mkdir -p $(dir $@) - $(CC) -c $(INCLUDES) $(DRI_CFLAGS) $(DEFINES) $< -o $@ - -glsl_lexer.cpp: glsl_lexer.ll - flex --nounistd -o$@ $< - -glsl_parser.cpp: glsl_parser.yy - bison -v -o "$@" -p "_mesa_glsl_" --defines=glsl_parser.h $< - -glcpp/glcpp-lex.c: glcpp/glcpp-lex.l - flex --nounistd -o$@ $< - -glcpp/glcpp-parse.c: glcpp/glcpp-parse.y - bison -v -o "$@" --defines=glcpp/glcpp-parse.h $< - -builtin_compiler: $(GLSL2_OBJECTS) $(OBJECTS) builtin_stubs.o - $(APP_CXX) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) $(GLSL2_OBJECTS) builtin_stubs.o -o $@ - -builtin_function.cpp: builtins/profiles/* builtins/ir/* builtins/tools/generate_builtins.py builtins/tools/texture_builtins.py builtin_compiler - @echo Regenerating builtin_function.cpp... - $(PYTHON2) $(PYTHON_FLAGS) builtins/tools/generate_builtins.py ./builtin_compiler > builtin_function.cpp || rm -f builtin_function.cpp - --include depend +
+#src/glsl/pp/Makefile
+
+TOP = ../..
+
+include $(TOP)/configs/current
+
+LIBNAME = glsl
+
+LIBGLCPP_SOURCES = \
+ glcpp/glcpp-lex.c \
+ glcpp/glcpp-parse.c \
+ glcpp/pp.c
+
+GLCPP_SOURCES = \
+ $(LIBGLCPP_SOURCES) \
+ ralloc.c \
+ glcpp/glcpp.c
+
+C_SOURCES = \
+ strtod.c \
+ ralloc.c \
+ $(LIBGLCPP_SOURCES)
+
+CXX_SOURCES = \
+ ast_expr.cpp \
+ ast_function.cpp \
+ ast_to_hir.cpp \
+ ast_type.cpp \
+ glsl_lexer.cpp \
+ glsl_parser.cpp \
+ glsl_parser_extras.cpp \
+ glsl_types.cpp \
+ glsl_symbol_table.cpp \
+ hir_field_selection.cpp \
+ ir_basic_block.cpp \
+ ir_clone.cpp \
+ ir_constant_expression.cpp \
+ ir.cpp \
+ ir_expression_flattening.cpp \
+ ir_function_can_inline.cpp \
+ ir_function.cpp \
+ ir_hierarchical_visitor.cpp \
+ ir_hv_accept.cpp \
+ ir_import_prototypes.cpp \
+ ir_print_visitor.cpp \
+ ir_reader.cpp \
+ ir_rvalue_visitor.cpp \
+ ir_set_program_inouts.cpp \
+ ir_validate.cpp \
+ ir_variable.cpp \
+ ir_variable_refcount.cpp \
+ linker.cpp \
+ link_functions.cpp \
+ loop_analysis.cpp \
+ loop_controls.cpp \
+ loop_unroll.cpp \
+ lower_discard.cpp \
+ lower_if_to_cond_assign.cpp \
+ lower_instructions.cpp \
+ lower_jumps.cpp \
+ lower_mat_op_to_vec.cpp \
+ lower_noise.cpp \
+ lower_texture_projection.cpp \
+ lower_variable_index_to_cond_assign.cpp \
+ lower_vec_index_to_cond_assign.cpp \
+ lower_vec_index_to_swizzle.cpp \
+ lower_vector.cpp \
+ opt_algebraic.cpp \
+ opt_constant_folding.cpp \
+ opt_constant_propagation.cpp \
+ opt_constant_variable.cpp \
+ opt_copy_propagation.cpp \
+ opt_copy_propagation_elements.cpp \
+ opt_dead_code.cpp \
+ opt_dead_code_local.cpp \
+ opt_dead_functions.cpp \
+ opt_discard_simplification.cpp \
+ opt_function_inlining.cpp \
+ opt_if_simplification.cpp \
+ opt_noop_swizzle.cpp \
+ opt_redundant_jumps.cpp \
+ opt_structure_splitting.cpp \
+ opt_swizzle_swizzle.cpp \
+ opt_tree_grafting.cpp \
+ s_expression.cpp
+
+LIBS = \
+ $(TOP)/src/glsl/libglsl.a
+
+APPS = glsl_compiler glcpp/glcpp
+
+GLSL2_C_SOURCES = \
+ ../mesa/program/hash_table.c \
+ ../mesa/program/symbol_table.c
+GLSL2_CXX_SOURCES = \
+ main.cpp
+
+GLSL2_OBJECTS = \
+ $(GLSL2_C_SOURCES:.c=.o) \
+ $(GLSL2_CXX_SOURCES:.cpp=.o)
+
+### Basic defines ###
+
+DEFINES += \
+ $(LIBRARY_DEFINES) \
+ $(API_DEFINES)
+
+GLCPP_OBJECTS = \
+ $(GLCPP_SOURCES:.c=.o) \
+ ../mesa/program/hash_table.o
+
+OBJECTS = \
+ $(C_SOURCES:.c=.o) \
+ $(CXX_SOURCES:.cpp=.o)
+
+DRICORE_OBJ_DIR = obj-visible
+OBJECTS_DRICORE = $(addprefix $(DRICORE_OBJ_DIR)/,$(OBJECTS))
+
+INCLUDES = \
+ -I. \
+ -I../mesa \
+ -I../mapi \
+ -I../../include \
+ $(LIBRARY_INCLUDES)
+
+ALL_SOURCES = \
+ $(C_SOURCES) \
+ $(CXX_SOURCES) \
+ $(GLSL2_CXX_SOURCES) \
+ $(GLSL2_C_SOURCES)
+
+##### TARGETS #####
+
+default: depend lib$(LIBNAME).a $(APPS) $(DRICORE_GLSL_LIBS)
+
+$(TOP)/$(LIB_DIR)/libglsl.so: $(OBJECTS_DRICORE) builtin_function.o Makefile $(TOP)/src/glsl/Makefile.template
+ $(MKLIB) -o $@ -linker '$(CXX)' -ldflags '$(LDFLAGS)' \
+ -cplusplus -noprefix \
+ -install $(TOP)/$(LIB_DIR) -id $(INSTALL_LIB_DIR)/$@.dylib \
+ $(OBJECTS_DRICORE) builtin_function.o
+
+lib$(LIBNAME).a: $(OBJECTS) builtin_function.o Makefile $(TOP)/src/glsl/Makefile.template
+ $(MKLIB) -cplusplus -o $(LIBNAME) -static $(OBJECTS) builtin_function.o
+
+depend: $(ALL_SOURCES) Makefile
+ rm -f depend
+ touch depend
+ $(MKDEP) $(MKDEP_OPTIONS) $(INCLUDES) $(ALL_SOURCES) 2> /dev/null
+
+# Remove .o and backup files
+clean: clean-dricore
+ rm -f $(GLCPP_OBJECTS) $(GLSL2_OBJECTS) $(OBJECTS) lib$(LIBNAME).a depend depend.bak builtin_function.cpp builtin_function.o builtin_stubs.o builtin_compiler
+ -rm -f $(APPS)
+
+clean-dricore:
+ -rm -f $(DRICORE_OBJ_DIR) $(TOP)/$(LIB_DIR)/libglsl.so libglsl.so
+
+ifneq (,$(DRICORE_GLSL_LIBS))
+DRICORE_INSTALL_TARGET = install-dricore
+endif
+
+# Dummy target
+install: $(DRICORE_INSTALL_TARGET)
+ @echo -n ""
+
+install-dricore: default
+ $(INSTALL) -d $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR)
+ $(INSTALL) -m 755 $(DRICORE_GLSL_LIBS) $(DESTDIR)$(DRI_DRIVER_INSTALL_DIR)
+
+##### RULES #####
+
+glsl_compiler: $(GLSL2_OBJECTS) libglsl.a builtin_stubs.o
+ $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) builtin_stubs.o $(LIBS) -o $@
+
+glcpp: glcpp/glcpp
+glcpp/glcpp: $(GLCPP_OBJECTS)
+ $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) -o $@
+
+.cpp.o:
+ $(CXX) -c $(INCLUDES) $(CXXFLAGS) $(DEFINES) $< -o $@
+
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(DEFINES) $< -o $@
+
+$(DRICORE_OBJ_DIR)/%.o : %.cpp
+ @mkdir -p $(dir $@)
+ $(CXX) -c $(INCLUDES) $(DRI_CXXFLAGS) $(DEFINES) $< -o $@
+
+$(DRICORE_OBJ_DIR)/%.o : %.c
+ @mkdir -p $(dir $@)
+ $(CC) -c $(INCLUDES) $(DRI_CFLAGS) $(DEFINES) $< -o $@
+
+glsl_lexer.cpp: glsl_lexer.ll
+ flex --nounistd -o$@ $<
+
+glsl_parser.cpp: glsl_parser.yy
+ bison -v -o "$@" -p "_mesa_glsl_" --defines=glsl_parser.h $<
+
+glcpp/glcpp-lex.c: glcpp/glcpp-lex.l
+ flex --nounistd -o$@ $<
+
+glcpp/glcpp-parse.c: glcpp/glcpp-parse.y
+ bison -v -o "$@" --defines=glcpp/glcpp-parse.h $<
+
+builtin_compiler: $(GLSL2_OBJECTS) $(OBJECTS) builtin_stubs.o
+ $(APP_CXX) $(INCLUDES) $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) $(GLSL2_OBJECTS) builtin_stubs.o -o $@
+
+builtin_function.cpp: builtins/profiles/* builtins/ir/* builtins/tools/generate_builtins.py builtins/tools/texture_builtins.py builtin_compiler
+ @echo Regenerating builtin_function.cpp...
+ $(PYTHON2) $(PYTHON_FLAGS) builtins/tools/generate_builtins.py ./builtin_compiler > builtin_function.cpp || rm -f builtin_function.cpp
+
+-include depend
diff --git a/mesalib/src/glsl/ast.h b/mesalib/src/glsl/ast.h index 878f48b20..6963b83af 100644 --- a/mesalib/src/glsl/ast.h +++ b/mesalib/src/glsl/ast.h @@ -63,6 +63,10 @@ public: /* If the user *does* call delete, that's OK, we will just * ralloc_free in that case. */ + static void operator delete(void *table, void *ctx) + { + ralloc_free(table); + } static void operator delete(void *table) { ralloc_free(table); diff --git a/mesalib/src/glsl/ast_expr.cpp b/mesalib/src/glsl/ast_expr.cpp index e624d11cf..e8812e7a1 100644 --- a/mesalib/src/glsl/ast_expr.cpp +++ b/mesalib/src/glsl/ast_expr.cpp @@ -1,95 +1,95 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include <assert.h> -#include "ast.h" - -const char * -ast_expression::operator_string(enum ast_operators op) -{ - static const char *const operators[] = { - "=", - "+", - "-", - "+", - "-", - "*", - "/", - "%", - "<<", - ">>", - "<", - ">", - "<=", - ">=", - "==", - "!=", - "&", - "^", - "|", - "~", - "&&", - "^^", - "||", - "!", - - "*=", - "/=", - "%=", - "+=", - "-=", - "<<=", - ">>=", - "&=", - "^=", - "|=", - - "?:", - - "++", - "--", - "++", - "--", - ".", - }; - - assert((unsigned int)op < sizeof(operators) / sizeof(operators[0])); - - return operators[op]; -} - - -ast_expression_bin::ast_expression_bin(int oper, ast_expression *ex0, - ast_expression *ex1) : - ast_expression(oper, ex0, ex1, NULL) -{ - assert((oper >= ast_plus) && (oper <= ast_logic_not)); -} - - -void -ast_expression_bin::print(void) const -{ - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - subexpressions[1]->print(); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include "ast.h"
+
+const char *
+ast_expression::operator_string(enum ast_operators op)
+{
+ static const char *const operators[] = {
+ "=",
+ "+",
+ "-",
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "<<",
+ ">>",
+ "<",
+ ">",
+ "<=",
+ ">=",
+ "==",
+ "!=",
+ "&",
+ "^",
+ "|",
+ "~",
+ "&&",
+ "^^",
+ "||",
+ "!",
+
+ "*=",
+ "/=",
+ "%=",
+ "+=",
+ "-=",
+ "<<=",
+ ">>=",
+ "&=",
+ "^=",
+ "|=",
+
+ "?:",
+
+ "++",
+ "--",
+ "++",
+ "--",
+ ".",
+ };
+
+ assert((unsigned int)op < sizeof(operators) / sizeof(operators[0]));
+
+ return operators[op];
+}
+
+
+ast_expression_bin::ast_expression_bin(int oper, ast_expression *ex0,
+ ast_expression *ex1) :
+ ast_expression(oper, ex0, ex1, NULL)
+{
+ assert((oper >= ast_plus) && (oper <= ast_logic_not));
+}
+
+
+void
+ast_expression_bin::print(void) const
+{
+ subexpressions[0]->print();
+ printf("%s ", operator_string(oper));
+ subexpressions[1]->print();
+}
diff --git a/mesalib/src/glsl/ast_to_hir.cpp b/mesalib/src/glsl/ast_to_hir.cpp index cdb16fd49..344d76b57 100644 --- a/mesalib/src/glsl/ast_to_hir.cpp +++ b/mesalib/src/glsl/ast_to_hir.cpp @@ -1,3502 +1,3502 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file ast_to_hir.c - * Convert abstract syntax to to high-level intermediate reprensentation (HIR). - * - * During the conversion to HIR, the majority of the symantic checking is - * preformed on the program. This includes: - * - * * Symbol table management - * * Type checking - * * Function binding - * - * The majority of this work could be done during parsing, and the parser could - * probably generate HIR directly. However, this results in frequent changes - * to the parser code. Since we do not assume that every system this complier - * is built on will have Flex and Bison installed, we have to store the code - * generated by these tools in our version control system. In other parts of - * the system we've seen problems where a parser was changed but the generated - * code was not committed, merge conflicts where created because two developers - * had slightly different versions of Bison installed, etc. - * - * I have also noticed that running Bison generated parsers in GDB is very - * irritating. When you get a segfault on '$$ = $1->foo', you can't very - * well 'print $1' in GDB. - * - * As a result, my preference is to put as little C code as possible in the - * parser (and lexer) sources. - */ - -#include "main/core.h" /* for struct gl_extensions */ -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "ast.h" -#include "glsl_types.h" -#include "ir.h" - -void -_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) -{ - _mesa_glsl_initialize_variables(instructions, state); - _mesa_glsl_initialize_functions(state); - - state->symbols->language_version = state->language_version; - - state->current_function = NULL; - - /* Section 4.2 of the GLSL 1.20 specification states: - * "The built-in functions are scoped in a scope outside the global scope - * users declare global variables in. That is, a shader's global scope, - * available for user-defined functions and global variables, is nested - * inside the scope containing the built-in functions." - * - * Since built-in functions like ftransform() access built-in variables, - * it follows that those must be in the outer scope as well. - * - * We push scope here to create this nesting effect...but don't pop. - * This way, a shader's globals are still in the symbol table for use - * by the linker. - */ - state->symbols->push_scope(); - - foreach_list_typed (ast_node, ast, link, & state->translation_unit) - ast->hir(instructions, state); -} - - -/** - * If a conversion is available, convert one operand to a different type - * - * The \c from \c ir_rvalue is converted "in place". - * - * \param to Type that the operand it to be converted to - * \param from Operand that is being converted - * \param state GLSL compiler state - * - * \return - * If a conversion is possible (or unnecessary), \c true is returned. - * Otherwise \c false is returned. - */ -bool -apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - if (to->base_type == from->type->base_type) - return true; - - /* This conversion was added in GLSL 1.20. If the compilation mode is - * GLSL 1.10, the conversion is skipped. - */ - if (state->language_version < 120) - return false; - - /* From page 27 (page 33 of the PDF) of the GLSL 1.50 spec: - * - * "There are no implicit array or structure conversions. For - * example, an array of int cannot be implicitly converted to an - * array of float. There are no implicit conversions between - * signed and unsigned integers." - */ - /* FINISHME: The above comment is partially a lie. There is int/uint - * FINISHME: conversion for immediate constants. - */ - if (!to->is_float() || !from->type->is_numeric()) - return false; - - /* Convert to a floating point type with the same number of components - * as the original type - i.e. int to float, not int to vec4. - */ - to = glsl_type::get_instance(GLSL_TYPE_FLOAT, from->type->vector_elements, - from->type->matrix_columns); - - switch (from->type->base_type) { - case GLSL_TYPE_INT: - from = new(ctx) ir_expression(ir_unop_i2f, to, from, NULL); - break; - case GLSL_TYPE_UINT: - from = new(ctx) ir_expression(ir_unop_u2f, to, from, NULL); - break; - case GLSL_TYPE_BOOL: - from = new(ctx) ir_expression(ir_unop_b2f, to, from, NULL); - break; - default: - assert(0); - } - - return true; -} - - -static const struct glsl_type * -arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, - bool multiply, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - const glsl_type *type_a = value_a->type; - const glsl_type *type_b = value_b->type; - - /* From GLSL 1.50 spec, page 56: - * - * "The arithmetic binary operators add (+), subtract (-), - * multiply (*), and divide (/) operate on integer and - * floating-point scalars, vectors, and matrices." - */ - if (!type_a->is_numeric() || !type_b->is_numeric()) { - _mesa_glsl_error(loc, state, - "Operands to arithmetic operators must be numeric"); - return glsl_type::error_type; - } - - - /* "If one operand is floating-point based and the other is - * not, then the conversions from Section 4.1.10 "Implicit - * Conversions" are applied to the non-floating-point-based operand." - */ - if (!apply_implicit_conversion(type_a, value_b, state) - && !apply_implicit_conversion(type_b, value_a, state)) { - _mesa_glsl_error(loc, state, - "Could not implicitly convert operands to " - "arithmetic operator"); - return glsl_type::error_type; - } - type_a = value_a->type; - type_b = value_b->type; - - /* "If the operands are integer types, they must both be signed or - * both be unsigned." - * - * From this rule and the preceeding conversion it can be inferred that - * both types must be GLSL_TYPE_FLOAT, or GLSL_TYPE_UINT, or GLSL_TYPE_INT. - * The is_numeric check above already filtered out the case where either - * type is not one of these, so now the base types need only be tested for - * equality. - */ - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, - "base type mismatch for arithmetic operator"); - return glsl_type::error_type; - } - - /* "All arithmetic binary operators result in the same fundamental type - * (signed integer, unsigned integer, or floating-point) as the - * operands they operate on, after operand type conversion. After - * conversion, the following cases are valid - * - * * The two operands are scalars. In this case the operation is - * applied, resulting in a scalar." - */ - if (type_a->is_scalar() && type_b->is_scalar()) - return type_a; - - /* "* One operand is a scalar, and the other is a vector or matrix. - * In this case, the scalar operation is applied independently to each - * component of the vector or matrix, resulting in the same size - * vector or matrix." - */ - if (type_a->is_scalar()) { - if (!type_b->is_scalar()) - return type_b; - } else if (type_b->is_scalar()) { - return type_a; - } - - /* All of the combinations of <scalar, scalar>, <vector, scalar>, - * <scalar, vector>, <scalar, matrix>, and <matrix, scalar> have been - * handled. - */ - assert(!type_a->is_scalar()); - assert(!type_b->is_scalar()); - - /* "* The two operands are vectors of the same size. In this case, the - * operation is done component-wise resulting in the same size - * vector." - */ - if (type_a->is_vector() && type_b->is_vector()) { - if (type_a == type_b) { - return type_a; - } else { - _mesa_glsl_error(loc, state, - "vector size mismatch for arithmetic operator"); - return glsl_type::error_type; - } - } - - /* All of the combinations of <scalar, scalar>, <vector, scalar>, - * <scalar, vector>, <scalar, matrix>, <matrix, scalar>, and - * <vector, vector> have been handled. At least one of the operands must - * be matrix. Further, since there are no integer matrix types, the base - * type of both operands must be float. - */ - assert(type_a->is_matrix() || type_b->is_matrix()); - assert(type_a->base_type == GLSL_TYPE_FLOAT); - assert(type_b->base_type == GLSL_TYPE_FLOAT); - - /* "* The operator is add (+), subtract (-), or divide (/), and the - * operands are matrices with the same number of rows and the same - * number of columns. In this case, the operation is done component- - * wise resulting in the same size matrix." - * * The operator is multiply (*), where both operands are matrices or - * one operand is a vector and the other a matrix. A right vector - * operand is treated as a column vector and a left vector operand as a - * row vector. In all these cases, it is required that the number of - * columns of the left operand is equal to the number of rows of the - * right operand. Then, the multiply (*) operation does a linear - * algebraic multiply, yielding an object that has the same number of - * rows as the left operand and the same number of columns as the right - * operand. Section 5.10 "Vector and Matrix Operations" explains in - * more detail how vectors and matrices are operated on." - */ - if (! multiply) { - if (type_a == type_b) - return type_a; - } else { - if (type_a->is_matrix() && type_b->is_matrix()) { - /* Matrix multiply. The columns of A must match the rows of B. Given - * the other previously tested constraints, this means the vector type - * of a row from A must be the same as the vector type of a column from - * B. - */ - if (type_a->row_type() == type_b->column_type()) { - /* The resulting matrix has the number of columns of matrix B and - * the number of rows of matrix A. We get the row count of A by - * looking at the size of a vector that makes up a column. The - * transpose (size of a row) is done for B. - */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_a->column_type()->vector_elements, - type_b->row_type()->vector_elements); - assert(type != glsl_type::error_type); - - return type; - } - } else if (type_a->is_matrix()) { - /* A is a matrix and B is a column vector. Columns of A must match - * rows of B. Given the other previously tested constraints, this - * means the vector type of a row from A must be the same as the - * vector the type of B. - */ - if (type_a->row_type() == type_b) { - /* The resulting vector has a number of elements equal to - * the number of rows of matrix A. */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_a->column_type()->vector_elements, - 1); - assert(type != glsl_type::error_type); - - return type; - } - } else { - assert(type_b->is_matrix()); - - /* A is a row vector and B is a matrix. Columns of A must match rows - * of B. Given the other previously tested constraints, this means - * the type of A must be the same as the vector type of a column from - * B. - */ - if (type_a == type_b->column_type()) { - /* The resulting vector has a number of elements equal to - * the number of columns of matrix B. */ - const glsl_type *const type = - glsl_type::get_instance(type_a->base_type, - type_b->row_type()->vector_elements, - 1); - assert(type != glsl_type::error_type); - - return type; - } - } - - _mesa_glsl_error(loc, state, "size mismatch for matrix multiplication"); - return glsl_type::error_type; - } - - - /* "All other cases are illegal." - */ - _mesa_glsl_error(loc, state, "type mismatch"); - return glsl_type::error_type; -} - - -static const struct glsl_type * -unary_arithmetic_result_type(const struct glsl_type *type, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - /* From GLSL 1.50 spec, page 57: - * - * "The arithmetic unary operators negate (-), post- and pre-increment - * and decrement (-- and ++) operate on integer or floating-point - * values (including vectors and matrices). All unary operators work - * component-wise on their operands. These result with the same type - * they operated on." - */ - if (!type->is_numeric()) { - _mesa_glsl_error(loc, state, - "Operands to arithmetic operators must be numeric"); - return glsl_type::error_type; - } - - return type; -} - -/** - * \brief Return the result type of a bit-logic operation. - * - * If the given types to the bit-logic operator are invalid, return - * glsl_type::error_type. - * - * \param type_a Type of LHS of bit-logic op - * \param type_b Type of RHS of bit-logic op - */ -static const struct glsl_type * -bit_logic_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - ast_operators op, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); - return glsl_type::error_type; - } - - /* From page 50 (page 56 of PDF) of GLSL 1.30 spec: - * - * "The bitwise operators and (&), exclusive-or (^), and inclusive-or - * (|). The operands must be of type signed or unsigned integers or - * integer vectors." - */ - if (!type_a->is_integer()) { - _mesa_glsl_error(loc, state, "LHS of `%s' must be an integer", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - if (!type_b->is_integer()) { - _mesa_glsl_error(loc, state, "RHS of `%s' must be an integer", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "The fundamental types of the operands (signed or unsigned) must - * match," - */ - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, "operands of `%s' must have the same " - "base type", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "The operands cannot be vectors of differing size." */ - if (type_a->is_vector() && - type_b->is_vector() && - type_a->vector_elements != type_b->vector_elements) { - _mesa_glsl_error(loc, state, "operands of `%s' cannot be vectors of " - "different sizes", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "If one operand is a scalar and the other a vector, the scalar is - * applied component-wise to the vector, resulting in the same type as - * the vector. The fundamental types of the operands [...] will be the - * resulting fundamental type." - */ - if (type_a->is_scalar()) - return type_b; - else - return type_a; -} - -static const struct glsl_type * -modulus_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, - "operator '%%' is reserved in %s", - state->version_string); - return glsl_type::error_type; - } - - /* From GLSL 1.50 spec, page 56: - * "The operator modulus (%) operates on signed or unsigned integers or - * integer vectors. The operand types must both be signed or both be - * unsigned." - */ - if (!type_a->is_integer() || !type_b->is_integer() - || (type_a->base_type != type_b->base_type)) { - _mesa_glsl_error(loc, state, "type mismatch"); - return glsl_type::error_type; - } - - /* "The operands cannot be vectors of differing size. If one operand is - * a scalar and the other vector, then the scalar is applied component- - * wise to the vector, resulting in the same type as the vector. If both - * are vectors of the same size, the result is computed component-wise." - */ - if (type_a->is_vector()) { - if (!type_b->is_vector() - || (type_a->vector_elements == type_b->vector_elements)) - return type_a; - } else - return type_b; - - /* "The operator modulus (%) is not defined for any other data types - * (non-integer types)." - */ - _mesa_glsl_error(loc, state, "type mismatch"); - return glsl_type::error_type; -} - - -static const struct glsl_type * -relational_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - const glsl_type *type_a = value_a->type; - const glsl_type *type_b = value_b->type; - - /* From GLSL 1.50 spec, page 56: - * "The relational operators greater than (>), less than (<), greater - * than or equal (>=), and less than or equal (<=) operate only on - * scalar integer and scalar floating-point expressions." - */ - if (!type_a->is_numeric() - || !type_b->is_numeric() - || !type_a->is_scalar() - || !type_b->is_scalar()) { - _mesa_glsl_error(loc, state, - "Operands to relational operators must be scalar and " - "numeric"); - return glsl_type::error_type; - } - - /* "Either the operands' types must match, or the conversions from - * Section 4.1.10 "Implicit Conversions" will be applied to the integer - * operand, after which the types must match." - */ - if (!apply_implicit_conversion(type_a, value_b, state) - && !apply_implicit_conversion(type_b, value_a, state)) { - _mesa_glsl_error(loc, state, - "Could not implicitly convert operands to " - "relational operator"); - return glsl_type::error_type; - } - type_a = value_a->type; - type_b = value_b->type; - - if (type_a->base_type != type_b->base_type) { - _mesa_glsl_error(loc, state, "base type mismatch"); - return glsl_type::error_type; - } - - /* "The result is scalar Boolean." - */ - return glsl_type::bool_type; -} - -/** - * \brief Return the result type of a bit-shift operation. - * - * If the given types to the bit-shift operator are invalid, return - * glsl_type::error_type. - * - * \param type_a Type of LHS of bit-shift op - * \param type_b Type of RHS of bit-shift op - */ -static const struct glsl_type * -shift_result_type(const struct glsl_type *type_a, - const struct glsl_type *type_b, - ast_operators op, - struct _mesa_glsl_parse_state *state, YYLTYPE *loc) -{ - if (state->language_version < 130) { - _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30"); - return glsl_type::error_type; - } - - /* From page 50 (page 56 of the PDF) of the GLSL 1.30 spec: - * - * "The shift operators (<<) and (>>). For both operators, the operands - * must be signed or unsigned integers or integer vectors. One operand - * can be signed while the other is unsigned." - */ - if (!type_a->is_integer()) { - _mesa_glsl_error(loc, state, "LHS of operator %s must be an integer or " - "integer vector", ast_expression::operator_string(op)); - return glsl_type::error_type; - - } - if (!type_b->is_integer()) { - _mesa_glsl_error(loc, state, "RHS of operator %s must be an integer or " - "integer vector", ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "If the first operand is a scalar, the second operand has to be - * a scalar as well." - */ - if (type_a->is_scalar() && !type_b->is_scalar()) { - _mesa_glsl_error(loc, state, "If the first operand of %s is scalar, the " - "second must be scalar as well", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* If both operands are vectors, check that they have same number of - * elements. - */ - if (type_a->is_vector() && - type_b->is_vector() && - type_a->vector_elements != type_b->vector_elements) { - _mesa_glsl_error(loc, state, "Vector operands to operator %s must " - "have same number of elements", - ast_expression::operator_string(op)); - return glsl_type::error_type; - } - - /* "In all cases, the resulting type will be the same type as the left - * operand." - */ - return type_a; -} - -/** - * Validates that a value can be assigned to a location with a specified type - * - * Validates that \c rhs can be assigned to some location. If the types are - * not an exact match but an automatic conversion is possible, \c rhs will be - * converted. - * - * \return - * \c NULL if \c rhs cannot be assigned to a location with type \c lhs_type. - * Otherwise the actual RHS to be assigned will be returned. This may be - * \c rhs, or it may be \c rhs after some type conversion. - * - * \note - * In addition to being used for assignments, this function is used to - * type-check return values. - */ -ir_rvalue * -validate_assignment(struct _mesa_glsl_parse_state *state, - const glsl_type *lhs_type, ir_rvalue *rhs, - bool is_initializer) -{ - /* If there is already some error in the RHS, just return it. Anything - * else will lead to an avalanche of error message back to the user. - */ - if (rhs->type->is_error()) - return rhs; - - /* If the types are identical, the assignment can trivially proceed. - */ - if (rhs->type == lhs_type) - return rhs; - - /* If the array element types are the same and the size of the LHS is zero, - * the assignment is okay for initializers embedded in variable - * declarations. - * - * Note: Whole-array assignments are not permitted in GLSL 1.10, but this - * is handled by ir_dereference::is_lvalue. - */ - if (is_initializer && lhs_type->is_array() && rhs->type->is_array() - && (lhs_type->element_type() == rhs->type->element_type()) - && (lhs_type->array_size() == 0)) { - return rhs; - } - - /* Check for implicit conversion in GLSL 1.20 */ - if (apply_implicit_conversion(lhs_type, rhs, state)) { - if (rhs->type == lhs_type) - return rhs; - } - - return NULL; -} - -ir_rvalue * -do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state, - ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer, - YYLTYPE lhs_loc) -{ - void *ctx = state; - bool error_emitted = (lhs->type->is_error() || rhs->type->is_error()); - - if (!error_emitted) { - if (lhs->variable_referenced() != NULL - && lhs->variable_referenced()->read_only) { - _mesa_glsl_error(&lhs_loc, state, - "assignment to read-only variable '%s'", - lhs->variable_referenced()->name); - error_emitted = true; - - } else if (!lhs->is_lvalue()) { - _mesa_glsl_error(& lhs_loc, state, "non-lvalue in assignment"); - error_emitted = true; - } - - if (state->es_shader && lhs->type->is_array()) { - _mesa_glsl_error(&lhs_loc, state, "whole array assignment is not " - "allowed in GLSL ES 1.00."); - error_emitted = true; - } - } - - ir_rvalue *new_rhs = - validate_assignment(state, lhs->type, rhs, is_initializer); - if (new_rhs == NULL) { - _mesa_glsl_error(& lhs_loc, state, "type mismatch"); - } else { - rhs = new_rhs; - - /* If the LHS array was not declared with a size, it takes it size from - * the RHS. If the LHS is an l-value and a whole array, it must be a - * dereference of a variable. Any other case would require that the LHS - * is either not an l-value or not a whole array. - */ - if (lhs->type->array_size() == 0) { - ir_dereference *const d = lhs->as_dereference(); - - assert(d != NULL); - - ir_variable *const var = d->variable_referenced(); - - assert(var != NULL); - - if (var->max_array_access >= unsigned(rhs->type->array_size())) { - /* FINISHME: This should actually log the location of the RHS. */ - _mesa_glsl_error(& lhs_loc, state, "array size must be > %u due to " - "previous access", - var->max_array_access); - } - - var->type = glsl_type::get_array_instance(lhs->type->element_type(), - rhs->type->array_size()); - d->type = var->type; - } - } - - /* Most callers of do_assignment (assign, add_assign, pre_inc/dec, - * but not post_inc) need the converted assigned value as an rvalue - * to handle things like: - * - * i = j += 1; - * - * So we always just store the computed value being assigned to a - * temporary and return a deref of that temporary. If the rvalue - * ends up not being used, the temp will get copy-propagated out. - */ - ir_variable *var = new(ctx) ir_variable(rhs->type, "assignment_tmp", - ir_var_temporary); - ir_dereference_variable *deref_var = new(ctx) ir_dereference_variable(var); - instructions->push_tail(var); - instructions->push_tail(new(ctx) ir_assignment(deref_var, - rhs, - NULL)); - deref_var = new(ctx) ir_dereference_variable(var); - - if (!error_emitted) - instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var, NULL)); - - return new(ctx) ir_dereference_variable(var); -} - -static ir_rvalue * -get_lvalue_copy(exec_list *instructions, ir_rvalue *lvalue) -{ - void *ctx = ralloc_parent(lvalue); - ir_variable *var; - - var = new(ctx) ir_variable(lvalue->type, "_post_incdec_tmp", - ir_var_temporary); - instructions->push_tail(var); - var->mode = ir_var_auto; - - instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var), - lvalue, NULL)); - - /* Once we've created this temporary, mark it read only so it's no - * longer considered an lvalue. - */ - var->read_only = true; - - return new(ctx) ir_dereference_variable(var); -} - - -ir_rvalue * -ast_node::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - (void) instructions; - (void) state; - - return NULL; -} - -static void -mark_whole_array_access(ir_rvalue *access) -{ - ir_dereference_variable *deref = access->as_dereference_variable(); - - if (deref) { - deref->var->max_array_access = deref->type->length - 1; - } -} - -static ir_rvalue * -do_comparison(void *mem_ctx, int operation, ir_rvalue *op0, ir_rvalue *op1) -{ - int join_op; - ir_rvalue *cmp = NULL; - - if (operation == ir_binop_all_equal) - join_op = ir_binop_logic_and; - else - join_op = ir_binop_logic_or; - - switch (op0->type->base_type) { - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_BOOL: - return new(mem_ctx) ir_expression(operation, op0, op1); - - case GLSL_TYPE_ARRAY: { - for (unsigned int i = 0; i < op0->type->length; i++) { - ir_rvalue *e0, *e1, *result; - - e0 = new(mem_ctx) ir_dereference_array(op0->clone(mem_ctx, NULL), - new(mem_ctx) ir_constant(i)); - e1 = new(mem_ctx) ir_dereference_array(op1->clone(mem_ctx, NULL), - new(mem_ctx) ir_constant(i)); - result = do_comparison(mem_ctx, operation, e0, e1); - - if (cmp) { - cmp = new(mem_ctx) ir_expression(join_op, cmp, result); - } else { - cmp = result; - } - } - - mark_whole_array_access(op0); - mark_whole_array_access(op1); - break; - } - - case GLSL_TYPE_STRUCT: { - for (unsigned int i = 0; i < op0->type->length; i++) { - ir_rvalue *e0, *e1, *result; - const char *field_name = op0->type->fields.structure[i].name; - - e0 = new(mem_ctx) ir_dereference_record(op0->clone(mem_ctx, NULL), - field_name); - e1 = new(mem_ctx) ir_dereference_record(op1->clone(mem_ctx, NULL), - field_name); - result = do_comparison(mem_ctx, operation, e0, e1); - - if (cmp) { - cmp = new(mem_ctx) ir_expression(join_op, cmp, result); - } else { - cmp = result; - } - } - break; - } - - case GLSL_TYPE_ERROR: - case GLSL_TYPE_VOID: - case GLSL_TYPE_SAMPLER: - /* I assume a comparison of a struct containing a sampler just - * ignores the sampler present in the type. - */ - break; - - default: - assert(!"Should not get here."); - break; - } - - if (cmp == NULL) - cmp = new(mem_ctx) ir_constant(true); - - return cmp; -} - -ir_rvalue * -ast_expression::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - static const int operations[AST_NUM_OPERATORS] = { - -1, /* ast_assign doesn't convert to ir_expression. */ - -1, /* ast_plus doesn't convert to ir_expression. */ - ir_unop_neg, - ir_binop_add, - ir_binop_sub, - ir_binop_mul, - ir_binop_div, - ir_binop_mod, - ir_binop_lshift, - ir_binop_rshift, - ir_binop_less, - ir_binop_greater, - ir_binop_lequal, - ir_binop_gequal, - ir_binop_all_equal, - ir_binop_any_nequal, - ir_binop_bit_and, - ir_binop_bit_xor, - ir_binop_bit_or, - ir_unop_bit_not, - ir_binop_logic_and, - ir_binop_logic_xor, - ir_binop_logic_or, - ir_unop_logic_not, - - /* Note: The following block of expression types actually convert - * to multiple IR instructions. - */ - ir_binop_mul, /* ast_mul_assign */ - ir_binop_div, /* ast_div_assign */ - ir_binop_mod, /* ast_mod_assign */ - ir_binop_add, /* ast_add_assign */ - ir_binop_sub, /* ast_sub_assign */ - ir_binop_lshift, /* ast_ls_assign */ - ir_binop_rshift, /* ast_rs_assign */ - ir_binop_bit_and, /* ast_and_assign */ - ir_binop_bit_xor, /* ast_xor_assign */ - ir_binop_bit_or, /* ast_or_assign */ - - -1, /* ast_conditional doesn't convert to ir_expression. */ - ir_binop_add, /* ast_pre_inc. */ - ir_binop_sub, /* ast_pre_dec. */ - ir_binop_add, /* ast_post_inc. */ - ir_binop_sub, /* ast_post_dec. */ - -1, /* ast_field_selection doesn't conv to ir_expression. */ - -1, /* ast_array_index doesn't convert to ir_expression. */ - -1, /* ast_function_call doesn't conv to ir_expression. */ - -1, /* ast_identifier doesn't convert to ir_expression. */ - -1, /* ast_int_constant doesn't convert to ir_expression. */ - -1, /* ast_uint_constant doesn't conv to ir_expression. */ - -1, /* ast_float_constant doesn't conv to ir_expression. */ - -1, /* ast_bool_constant doesn't conv to ir_expression. */ - -1, /* ast_sequence doesn't convert to ir_expression. */ - }; - ir_rvalue *result = NULL; - ir_rvalue *op[3]; - const struct glsl_type *type = glsl_type::error_type; - bool error_emitted = false; - YYLTYPE loc; - - loc = this->get_location(); - - switch (this->oper) { - case ast_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - result = do_assignment(instructions, state, op[0], op[1], false, - this->subexpressions[0]->get_location()); - error_emitted = result->type->is_error(); - type = result->type; - break; - } - - case ast_plus: - op[0] = this->subexpressions[0]->hir(instructions, state); - - type = unary_arithmetic_result_type(op[0]->type, state, & loc); - - error_emitted = type->is_error(); - - result = op[0]; - break; - - case ast_neg: - op[0] = this->subexpressions[0]->hir(instructions, state); - - type = unary_arithmetic_result_type(op[0]->type, state, & loc); - - error_emitted = type->is_error(); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], NULL); - break; - - case ast_add: - case ast_sub: - case ast_mul: - case ast_div: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = arithmetic_result_type(op[0], op[1], - (this->oper == ast_mul), - state, & loc); - error_emitted = type->is_error(); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - break; - - case ast_mod: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); - - assert(operations[this->oper] == ir_binop_mod); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = type->is_error(); - break; - - case ast_lshift: - case ast_rshift: - if (state->language_version < 130) { - _mesa_glsl_error(&loc, state, "operator %s requires GLSL 1.30", - operator_string(this->oper)); - error_emitted = true; - } - - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, - &loc); - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - - case ast_less: - case ast_greater: - case ast_lequal: - case ast_gequal: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = relational_result_type(op[0], op[1], state, & loc); - - /* The relational operators must either generate an error or result - * in a scalar boolean. See page 57 of the GLSL 1.50 spec. - */ - assert(type->is_error() - || ((type->base_type == GLSL_TYPE_BOOL) - && type->is_scalar())); - - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = type->is_error(); - break; - - case ast_nequal: - case ast_equal: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - /* From page 58 (page 64 of the PDF) of the GLSL 1.50 spec: - * - * "The equality operators equal (==), and not equal (!=) - * operate on all types. They result in a scalar Boolean. If - * the operand types do not match, then there must be a - * conversion from Section 4.1.10 "Implicit Conversions" - * applied to one operand that can make them match, in which - * case this conversion is done." - */ - if ((!apply_implicit_conversion(op[0]->type, op[1], state) - && !apply_implicit_conversion(op[1]->type, op[0], state)) - || (op[0]->type != op[1]->type)) { - _mesa_glsl_error(& loc, state, "operands of `%s' must have the same " - "type", (this->oper == ast_equal) ? "==" : "!="); - error_emitted = true; - } else if ((state->language_version <= 110) - && (op[0]->type->is_array() || op[1]->type->is_array())) { - _mesa_glsl_error(& loc, state, "array comparisons forbidden in " - "GLSL 1.10"); - error_emitted = true; - } - - result = do_comparison(ctx, operations[this->oper], op[0], op[1]); - type = glsl_type::bool_type; - - assert(error_emitted || (result->type == glsl_type::bool_type)); - break; - - case ast_bit_and: - case ast_bit_xor: - case ast_bit_or: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, - state, &loc); - result = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - - case ast_bit_not: - op[0] = this->subexpressions[0]->hir(instructions, state); - - if (state->language_version < 130) { - _mesa_glsl_error(&loc, state, "bit-wise operations require GLSL 1.30"); - error_emitted = true; - } - - if (!op[0]->type->is_integer()) { - _mesa_glsl_error(&loc, state, "operand of `~' must be an integer"); - error_emitted = true; - } - - type = op[0]->type; - result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL); - break; - - case ast_logic_and: { - op[0] = this->subexpressions[0]->hir(instructions, state); - - if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[0]->get_location(); - - _mesa_glsl_error(& loc, state, "LHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - - ir_constant *op0_const = op[0]->constant_expression_value(); - if (op0_const) { - if (op0_const->value.b[0]) { - op[1] = this->subexpressions[1]->hir(instructions, state); - - if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, - "RHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - result = op[1]; - } else { - result = op0_const; - } - type = glsl_type::bool_type; - } else { - ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, - "and_tmp", - ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - op[1] = this->subexpressions[1]->hir(&stmt->then_instructions, state); - - if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, - "RHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - - ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, op[1], NULL); - stmt->then_instructions.push_tail(then_assign); - - ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false), NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - type = tmp->type; - } - break; - } - - case ast_logic_or: { - op[0] = this->subexpressions[0]->hir(instructions, state); - - if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[0]->get_location(); - - _mesa_glsl_error(& loc, state, "LHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - - ir_constant *op0_const = op[0]->constant_expression_value(); - if (op0_const) { - if (op0_const->value.b[0]) { - result = op0_const; - } else { - op[1] = this->subexpressions[1]->hir(instructions, state); - - if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, - "RHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - result = op[1]; - } - type = glsl_type::bool_type; - } else { - ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type, - "or_tmp", - ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - op[1] = this->subexpressions[1]->hir(&stmt->else_instructions, state); - - if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, "RHS of `%s' must be scalar boolean", - operator_string(this->oper)); - error_emitted = true; - } - - ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true), NULL); - stmt->then_instructions.push_tail(then_assign); - - ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, op[1], NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - type = tmp->type; - } - break; - } - - case ast_logic_xor: - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - - result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, - op[0], op[1]); - type = glsl_type::bool_type; - break; - - case ast_logic_not: - op[0] = this->subexpressions[0]->hir(instructions, state); - - if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[0]->get_location(); - - _mesa_glsl_error(& loc, state, - "operand of `!' must be scalar boolean"); - error_emitted = true; - } - - result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type, - op[0], NULL); - type = glsl_type::bool_type; - break; - - case ast_mul_assign: - case ast_div_assign: - case ast_add_assign: - case ast_sub_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = arithmetic_result_type(op[0], op[1], - (this->oper == ast_mul_assign), - state, & loc); - - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - type = result->type; - error_emitted = (op[0]->type->is_error()); - - /* GLSL 1.10 does not allow array assignment. However, we don't have to - * explicitly test for this because none of the binary expression - * operators allow array operands either. - */ - - break; - } - - case ast_mod_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - - type = modulus_result_type(op[0]->type, op[1]->type, state, & loc); - - assert(operations[this->oper] == ir_binop_mod); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - type = result->type; - error_emitted = type->is_error(); - break; - } - - case ast_ls_assign: - case ast_rs_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = shift_result_type(op[0]->type, op[1]->type, this->oper, state, - &loc); - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], - type, op[0], op[1]); - result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), - temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - } - - case ast_and_assign: - case ast_xor_assign: - case ast_or_assign: { - op[0] = this->subexpressions[0]->hir(instructions, state); - op[1] = this->subexpressions[1]->hir(instructions, state); - type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper, - state, &loc); - ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], - type, op[0], op[1]); - result = do_assignment(instructions, state, op[0]->clone(ctx, NULL), - temp_rhs, false, - this->subexpressions[0]->get_location()); - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - break; - } - - case ast_conditional: { - op[0] = this->subexpressions[0]->hir(instructions, state); - - /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: - * - * "The ternary selection operator (?:). It operates on three - * expressions (exp1 ? exp2 : exp3). This operator evaluates the - * first expression, which must result in a scalar Boolean." - */ - if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) { - YYLTYPE loc = this->subexpressions[0]->get_location(); - - _mesa_glsl_error(& loc, state, "?: condition must be scalar boolean"); - error_emitted = true; - } - - /* The :? operator is implemented by generating an anonymous temporary - * followed by an if-statement. The last instruction in each branch of - * the if-statement assigns a value to the anonymous temporary. This - * temporary is the r-value of the expression. - */ - exec_list then_instructions; - exec_list else_instructions; - - op[1] = this->subexpressions[1]->hir(&then_instructions, state); - op[2] = this->subexpressions[2]->hir(&else_instructions, state); - - /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec: - * - * "The second and third expressions can be any type, as - * long their types match, or there is a conversion in - * Section 4.1.10 "Implicit Conversions" that can be applied - * to one of the expressions to make their types match. This - * resulting matching type is the type of the entire - * expression." - */ - if ((!apply_implicit_conversion(op[1]->type, op[2], state) - && !apply_implicit_conversion(op[2]->type, op[1], state)) - || (op[1]->type != op[2]->type)) { - YYLTYPE loc = this->subexpressions[1]->get_location(); - - _mesa_glsl_error(& loc, state, "Second and third operands of ?: " - "operator must have matching types."); - error_emitted = true; - type = glsl_type::error_type; - } else { - type = op[1]->type; - } - - /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec: - * - * "The second and third expressions must be the same type, but can - * be of any type other than an array." - */ - if ((state->language_version <= 110) && type->is_array()) { - _mesa_glsl_error(& loc, state, "Second and third operands of ?: " - "operator must not be arrays."); - error_emitted = true; - } - - ir_constant *cond_val = op[0]->constant_expression_value(); - ir_constant *then_val = op[1]->constant_expression_value(); - ir_constant *else_val = op[2]->constant_expression_value(); - - if (then_instructions.is_empty() - && else_instructions.is_empty() - && (cond_val != NULL) && (then_val != NULL) && (else_val != NULL)) { - result = (cond_val->value.b[0]) ? then_val : else_val; - } else { - ir_variable *const tmp = - new(ctx) ir_variable(type, "conditional_tmp", ir_var_temporary); - instructions->push_tail(tmp); - - ir_if *const stmt = new(ctx) ir_if(op[0]); - instructions->push_tail(stmt); - - then_instructions.move_nodes_to(& stmt->then_instructions); - ir_dereference *const then_deref = - new(ctx) ir_dereference_variable(tmp); - ir_assignment *const then_assign = - new(ctx) ir_assignment(then_deref, op[1], NULL); - stmt->then_instructions.push_tail(then_assign); - - else_instructions.move_nodes_to(& stmt->else_instructions); - ir_dereference *const else_deref = - new(ctx) ir_dereference_variable(tmp); - ir_assignment *const else_assign = - new(ctx) ir_assignment(else_deref, op[2], NULL); - stmt->else_instructions.push_tail(else_assign); - - result = new(ctx) ir_dereference_variable(tmp); - } - break; - } - - case ast_pre_inc: - case ast_pre_dec: { - op[0] = this->subexpressions[0]->hir(instructions, state); - if (op[0]->type->base_type == GLSL_TYPE_FLOAT) - op[1] = new(ctx) ir_constant(1.0f); - else - op[1] = new(ctx) ir_constant(1); - - type = arithmetic_result_type(op[0], op[1], false, state, & loc); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - result = do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - type = result->type; - error_emitted = op[0]->type->is_error(); - break; - } - - case ast_post_inc: - case ast_post_dec: { - op[0] = this->subexpressions[0]->hir(instructions, state); - if (op[0]->type->base_type == GLSL_TYPE_FLOAT) - op[1] = new(ctx) ir_constant(1.0f); - else - op[1] = new(ctx) ir_constant(1); - - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - - type = arithmetic_result_type(op[0], op[1], false, state, & loc); - - ir_rvalue *temp_rhs; - temp_rhs = new(ctx) ir_expression(operations[this->oper], type, - op[0], op[1]); - - /* Get a temporary of a copy of the lvalue before it's modified. - * This may get thrown away later. - */ - result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL)); - - (void)do_assignment(instructions, state, - op[0]->clone(ctx, NULL), temp_rhs, false, - this->subexpressions[0]->get_location()); - - type = result->type; - error_emitted = op[0]->type->is_error(); - break; - } - - case ast_field_selection: - result = _mesa_ast_field_selection_to_hir(this, instructions, state); - type = result->type; - break; - - case ast_array_index: { - YYLTYPE index_loc = subexpressions[1]->get_location(); - - op[0] = subexpressions[0]->hir(instructions, state); - op[1] = subexpressions[1]->hir(instructions, state); - - error_emitted = op[0]->type->is_error() || op[1]->type->is_error(); - - ir_rvalue *const array = op[0]; - - result = new(ctx) ir_dereference_array(op[0], op[1]); - - /* Do not use op[0] after this point. Use array. - */ - op[0] = NULL; - - - if (error_emitted) - break; - - if (!array->type->is_array() - && !array->type->is_matrix() - && !array->type->is_vector()) { - _mesa_glsl_error(& index_loc, state, - "cannot dereference non-array / non-matrix / " - "non-vector"); - error_emitted = true; - } - - if (!op[1]->type->is_integer()) { - _mesa_glsl_error(& index_loc, state, - "array index must be integer type"); - error_emitted = true; - } else if (!op[1]->type->is_scalar()) { - _mesa_glsl_error(& index_loc, state, - "array index must be scalar"); - error_emitted = true; - } - - /* If the array index is a constant expression and the array has a - * declared size, ensure that the access is in-bounds. If the array - * index is not a constant expression, ensure that the array has a - * declared size. - */ - ir_constant *const const_index = op[1]->constant_expression_value(); - if (const_index != NULL) { - const int idx = const_index->value.i[0]; - const char *type_name; - unsigned bound = 0; - - if (array->type->is_matrix()) { - type_name = "matrix"; - } else if (array->type->is_vector()) { - type_name = "vector"; - } else { - type_name = "array"; - } - - /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec: - * - * "It is illegal to declare an array with a size, and then - * later (in the same shader) index the same array with an - * integral constant expression greater than or equal to the - * declared size. It is also illegal to index an array with a - * negative constant expression." - */ - if (array->type->is_matrix()) { - if (array->type->row_type()->vector_elements <= idx) { - bound = array->type->row_type()->vector_elements; - } - } else if (array->type->is_vector()) { - if (array->type->vector_elements <= idx) { - bound = array->type->vector_elements; - } - } else { - if ((array->type->array_size() > 0) - && (array->type->array_size() <= idx)) { - bound = array->type->array_size(); - } - } - - if (bound > 0) { - _mesa_glsl_error(& loc, state, "%s index must be < %u", - type_name, bound); - error_emitted = true; - } else if (idx < 0) { - _mesa_glsl_error(& loc, state, "%s index must be >= 0", - type_name); - error_emitted = true; - } - - if (array->type->is_array()) { - /* If the array is a variable dereference, it dereferences the - * whole array, by definition. Use this to get the variable. - * - * FINISHME: Should some methods for getting / setting / testing - * FINISHME: array access limits be added to ir_dereference? - */ - ir_variable *const v = array->whole_variable_referenced(); - if ((v != NULL) && (unsigned(idx) > v->max_array_access)) - v->max_array_access = idx; - } - } else if (array->type->array_size() == 0) { - _mesa_glsl_error(&loc, state, "unsized array index must be constant"); - } else { - if (array->type->is_array()) { - /* whole_variable_referenced can return NULL if the array is a - * member of a structure. In this case it is safe to not update - * the max_array_access field because it is never used for fields - * of structures. - */ - ir_variable *v = array->whole_variable_referenced(); - if (v != NULL) - v->max_array_access = array->type->array_size(); - } - } - - /* From page 23 (29 of the PDF) of the GLSL 1.30 spec: - * - * "Samplers aggregated into arrays within a shader (using square - * brackets [ ]) can only be indexed with integral constant - * expressions [...]." - * - * This restriction was added in GLSL 1.30. Shaders using earlier version - * of the language should not be rejected by the compiler front-end for - * using this construct. This allows useful things such as using a loop - * counter as the index to an array of samplers. If the loop in unrolled, - * the code should compile correctly. Instead, emit a warning. - */ - if (array->type->is_array() && - array->type->element_type()->is_sampler() && - const_index == NULL) { - - if (state->language_version == 100) { - _mesa_glsl_warning(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is optional in GLSL ES 1.00"); - } else if (state->language_version < 130) { - _mesa_glsl_warning(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is forbidden in GLSL 1.30 and " - "later"); - } else { - _mesa_glsl_error(&loc, state, - "sampler arrays indexed with non-constant " - "expressions is forbidden in GLSL 1.30 and " - "later"); - error_emitted = true; - } - } - - if (error_emitted) - result->type = glsl_type::error_type; - - type = result->type; - break; - } - - case ast_function_call: - /* Should *NEVER* get here. ast_function_call should always be handled - * by ast_function_expression::hir. - */ - assert(0); - break; - - case ast_identifier: { - /* ast_identifier can appear several places in a full abstract syntax - * tree. This particular use must be at location specified in the grammar - * as 'variable_identifier'. - */ - ir_variable *var = - state->symbols->get_variable(this->primary_expression.identifier); - - result = new(ctx) ir_dereference_variable(var); - - if (var != NULL) { - var->used = true; - type = result->type; - } else { - _mesa_glsl_error(& loc, state, "`%s' undeclared", - this->primary_expression.identifier); - - error_emitted = true; - } - break; - } - - case ast_int_constant: - type = glsl_type::int_type; - result = new(ctx) ir_constant(this->primary_expression.int_constant); - break; - - case ast_uint_constant: - type = glsl_type::uint_type; - result = new(ctx) ir_constant(this->primary_expression.uint_constant); - break; - - case ast_float_constant: - type = glsl_type::float_type; - result = new(ctx) ir_constant(this->primary_expression.float_constant); - break; - - case ast_bool_constant: - type = glsl_type::bool_type; - result = new(ctx) ir_constant(bool(this->primary_expression.bool_constant)); - break; - - case ast_sequence: { - /* It should not be possible to generate a sequence in the AST without - * any expressions in it. - */ - assert(!this->expressions.is_empty()); - - /* The r-value of a sequence is the last expression in the sequence. If - * the other expressions in the sequence do not have side-effects (and - * therefore add instructions to the instruction list), they get dropped - * on the floor. - */ - foreach_list_typed (ast_node, ast, link, &this->expressions) - result = ast->hir(instructions, state); - - type = result->type; - - /* Any errors should have already been emitted in the loop above. - */ - error_emitted = true; - break; - } - } - - if (type->is_error() && !error_emitted) - _mesa_glsl_error(& loc, state, "type mismatch"); - - return result; -} - - -ir_rvalue * -ast_expression_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - /* It is possible to have expression statements that don't have an - * expression. This is the solitary semicolon: - * - * for (i = 0; i < 5; i++) - * ; - * - * In this case the expression will be NULL. Test for NULL and don't do - * anything in that case. - */ - if (expression != NULL) - expression->hir(instructions, state); - - /* Statements do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_compound_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - if (new_scope) - state->symbols->push_scope(); - - foreach_list_typed (ast_node, ast, link, &this->statements) - ast->hir(instructions, state); - - if (new_scope) - state->symbols->pop_scope(); - - /* Compound statements do not have r-values. - */ - return NULL; -} - - -static const glsl_type * -process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size, - struct _mesa_glsl_parse_state *state) -{ - unsigned length = 0; - - /* FINISHME: Reject delcarations of multidimensional arrays. */ - - if (array_size != NULL) { - exec_list dummy_instructions; - ir_rvalue *const ir = array_size->hir(& dummy_instructions, state); - YYLTYPE loc = array_size->get_location(); - - /* FINISHME: Verify that the grammar forbids side-effects in array - * FINISHME: sizes. i.e., 'vec4 [x = 12] data' - */ - assert(dummy_instructions.is_empty()); - - if (ir != NULL) { - if (!ir->type->is_integer()) { - _mesa_glsl_error(& loc, state, "array size must be integer type"); - } else if (!ir->type->is_scalar()) { - _mesa_glsl_error(& loc, state, "array size must be scalar type"); - } else { - ir_constant *const size = ir->constant_expression_value(); - - if (size == NULL) { - _mesa_glsl_error(& loc, state, "array size must be a " - "constant valued expression"); - } else if (size->value.i[0] <= 0) { - _mesa_glsl_error(& loc, state, "array size must be > 0"); - } else { - assert(size->type == ir->type); - length = size->value.u[0]; - } - } - } - } else if (state->es_shader) { - /* Section 10.17 of the GLSL ES 1.00 specification states that unsized - * array declarations have been removed from the language. - */ - _mesa_glsl_error(loc, state, "unsized array declarations are not " - "allowed in GLSL ES 1.00."); - } - - return glsl_type::get_array_instance(base, length); -} - - -const glsl_type * -ast_type_specifier::glsl_type(const char **name, - struct _mesa_glsl_parse_state *state) const -{ - const struct glsl_type *type; - - type = state->symbols->get_type(this->type_name); - *name = this->type_name; - - if (this->is_array) { - YYLTYPE loc = this->get_location(); - type = process_array_type(&loc, type, this->array_size, state); - } - - return type; -} - - -static void -apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual, - ir_variable *var, - struct _mesa_glsl_parse_state *state, - YYLTYPE *loc) -{ - if (qual->flags.q.invariant) { - if (var->used) { - _mesa_glsl_error(loc, state, - "variable `%s' may not be redeclared " - "`invariant' after being used", - var->name); - } else { - var->invariant = 1; - } - } - - if (qual->flags.q.constant || qual->flags.q.attribute - || qual->flags.q.uniform - || (qual->flags.q.varying && (state->target == fragment_shader))) - var->read_only = 1; - - if (qual->flags.q.centroid) - var->centroid = 1; - - if (qual->flags.q.attribute && state->target != vertex_shader) { - var->type = glsl_type::error_type; - _mesa_glsl_error(loc, state, - "`attribute' variables may not be declared in the " - "%s shader", - _mesa_glsl_shader_target_name(state->target)); - } - - /* From page 25 (page 31 of the PDF) of the GLSL 1.10 spec: - * - * "The varying qualifier can be used only with the data types - * float, vec2, vec3, vec4, mat2, mat3, and mat4, or arrays of - * these." - */ - if (qual->flags.q.varying) { - const glsl_type *non_array_type; - - if (var->type && var->type->is_array()) - non_array_type = var->type->fields.array; - else - non_array_type = var->type; - - if (non_array_type && non_array_type->base_type != GLSL_TYPE_FLOAT) { - var->type = glsl_type::error_type; - _mesa_glsl_error(loc, state, - "varying variables must be of base type float"); - } - } - - /* If there is no qualifier that changes the mode of the variable, leave - * the setting alone. - */ - if (qual->flags.q.in && qual->flags.q.out) - var->mode = ir_var_inout; - else if (qual->flags.q.attribute || qual->flags.q.in - || (qual->flags.q.varying && (state->target == fragment_shader))) - var->mode = ir_var_in; - else if (qual->flags.q.out - || (qual->flags.q.varying && (state->target == vertex_shader))) - var->mode = ir_var_out; - else if (qual->flags.q.uniform) - var->mode = ir_var_uniform; - - if (state->all_invariant && (state->current_function == NULL)) { - switch (state->target) { - case vertex_shader: - if (var->mode == ir_var_out) - var->invariant = true; - break; - case geometry_shader: - if ((var->mode == ir_var_in) || (var->mode == ir_var_out)) - var->invariant = true; - break; - case fragment_shader: - if (var->mode == ir_var_in) - var->invariant = true; - break; - } - } - - if (qual->flags.q.flat) - var->interpolation = ir_var_flat; - else if (qual->flags.q.noperspective) - var->interpolation = ir_var_noperspective; - else - var->interpolation = ir_var_smooth; - - var->pixel_center_integer = qual->flags.q.pixel_center_integer; - var->origin_upper_left = qual->flags.q.origin_upper_left; - if ((qual->flags.q.origin_upper_left || qual->flags.q.pixel_center_integer) - && (strcmp(var->name, "gl_FragCoord") != 0)) { - const char *const qual_string = (qual->flags.q.origin_upper_left) - ? "origin_upper_left" : "pixel_center_integer"; - - _mesa_glsl_error(loc, state, - "layout qualifier `%s' can only be applied to " - "fragment shader input `gl_FragCoord'", - qual_string); - } - - if (qual->flags.q.explicit_location) { - const bool global_scope = (state->current_function == NULL); - bool fail = false; - const char *string = ""; - - /* In the vertex shader only shader inputs can be given explicit - * locations. - * - * In the fragment shader only shader outputs can be given explicit - * locations. - */ - switch (state->target) { - case vertex_shader: - if (!global_scope || (var->mode != ir_var_in)) { - fail = true; - string = "input"; - } - break; - - case geometry_shader: - _mesa_glsl_error(loc, state, - "geometry shader variables cannot be given " - "explicit locations\n"); - break; - - case fragment_shader: - if (!global_scope || (var->mode != ir_var_in)) { - fail = true; - string = "output"; - } - break; - }; - - if (fail) { - _mesa_glsl_error(loc, state, - "only %s shader %s variables can be given an " - "explicit location\n", - _mesa_glsl_shader_target_name(state->target), - string); - } else { - var->explicit_location = true; - - /* This bit of silliness is needed because invalid explicit locations - * are supposed to be flagged during linking. Small negative values - * biased by VERT_ATTRIB_GENERIC0 or FRAG_RESULT_DATA0 could alias - * built-in values (e.g., -16+VERT_ATTRIB_GENERIC0 = VERT_ATTRIB_POS). - * The linker needs to be able to differentiate these cases. This - * ensures that negative values stay negative. - */ - if (qual->location >= 0) { - var->location = (state->target == vertex_shader) - ? (qual->location + VERT_ATTRIB_GENERIC0) - : (qual->location + FRAG_RESULT_DATA0); - } else { - var->location = qual->location; - } - } - } - - /* Does the declaration use the 'layout' keyword? - */ - const bool uses_layout = qual->flags.q.pixel_center_integer - || qual->flags.q.origin_upper_left - || qual->flags.q.explicit_location; - - /* Does the declaration use the deprecated 'attribute' or 'varying' - * keywords? - */ - const bool uses_deprecated_qualifier = qual->flags.q.attribute - || qual->flags.q.varying; - - /* Is the 'layout' keyword used with parameters that allow relaxed checking. - * Many implementations of GL_ARB_fragment_coord_conventions_enable and some - * implementations (only Mesa?) GL_ARB_explicit_attrib_location_enable - * allowed the layout qualifier to be used with 'varying' and 'attribute'. - * These extensions and all following extensions that add the 'layout' - * keyword have been modified to require the use of 'in' or 'out'. - * - * The following extension do not allow the deprecated keywords: - * - * GL_AMD_conservative_depth - * GL_ARB_gpu_shader5 - * GL_ARB_separate_shader_objects - * GL_ARB_tesselation_shader - * GL_ARB_transform_feedback3 - * GL_ARB_uniform_buffer_object - * - * It is unknown whether GL_EXT_shader_image_load_store or GL_NV_gpu_shader5 - * allow layout with the deprecated keywords. - */ - const bool relaxed_layout_qualifier_checking = - state->ARB_fragment_coord_conventions_enable; - - if (uses_layout && uses_deprecated_qualifier) { - if (relaxed_layout_qualifier_checking) { - _mesa_glsl_warning(loc, state, - "`layout' qualifier may not be used with " - "`attribute' or `varying'"); - } else { - _mesa_glsl_error(loc, state, - "`layout' qualifier may not be used with " - "`attribute' or `varying'"); - } - } - - /* Layout qualifiers for gl_FragDepth, which are enabled by extension - * AMD_conservative_depth. - */ - int depth_layout_count = qual->flags.q.depth_any - + qual->flags.q.depth_greater - + qual->flags.q.depth_less - + qual->flags.q.depth_unchanged; - if (depth_layout_count > 0 - && !state->AMD_conservative_depth_enable) { - _mesa_glsl_error(loc, state, - "extension GL_AMD_conservative_depth must be enabled " - "to use depth layout qualifiers"); - } else if (depth_layout_count > 0 - && strcmp(var->name, "gl_FragDepth") != 0) { - _mesa_glsl_error(loc, state, - "depth layout qualifiers can be applied only to " - "gl_FragDepth"); - } else if (depth_layout_count > 1 - && strcmp(var->name, "gl_FragDepth") == 0) { - _mesa_glsl_error(loc, state, - "at most one depth layout qualifier can be applied to " - "gl_FragDepth"); - } - if (qual->flags.q.depth_any) - var->depth_layout = ir_depth_layout_any; - else if (qual->flags.q.depth_greater) - var->depth_layout = ir_depth_layout_greater; - else if (qual->flags.q.depth_less) - var->depth_layout = ir_depth_layout_less; - else if (qual->flags.q.depth_unchanged) - var->depth_layout = ir_depth_layout_unchanged; - else - var->depth_layout = ir_depth_layout_none; - - if (var->type->is_array() && state->language_version != 110) { - var->array_lvalue = true; - } -} - -/** - * Get the variable that is being redeclared by this declaration - * - * Semantic checks to verify the validity of the redeclaration are also - * performed. If semantic checks fail, compilation error will be emitted via - * \c _mesa_glsl_error, but a non-\c NULL pointer will still be returned. - * - * \returns - * A pointer to an existing variable in the current scope if the declaration - * is a redeclaration, \c NULL otherwise. - */ -ir_variable * -get_variable_being_redeclared(ir_variable *var, ast_declaration *decl, - struct _mesa_glsl_parse_state *state) -{ - /* Check if this declaration is actually a re-declaration, either to - * resize an array or add qualifiers to an existing variable. - * - * This is allowed for variables in the current scope, or when at - * global scope (for built-ins in the implicit outer scope). - */ - ir_variable *earlier = state->symbols->get_variable(decl->identifier); - if (earlier == NULL || - (state->current_function != NULL && - !state->symbols->name_declared_this_scope(decl->identifier))) { - return NULL; - } - - - YYLTYPE loc = decl->get_location(); - - /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec, - * - * "It is legal to declare an array without a size and then - * later re-declare the same name as an array of the same - * type and specify a size." - */ - if ((earlier->type->array_size() == 0) - && var->type->is_array() - && (var->type->element_type() == earlier->type->element_type())) { - /* FINISHME: This doesn't match the qualifiers on the two - * FINISHME: declarations. It's not 100% clear whether this is - * FINISHME: required or not. - */ - - /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec: - * - * "The size [of gl_TexCoord] can be at most - * gl_MaxTextureCoords." - */ - const unsigned size = unsigned(var->type->array_size()); - if ((strcmp("gl_TexCoord", var->name) == 0) - && (size > state->Const.MaxTextureCoords)) { - _mesa_glsl_error(& loc, state, "`gl_TexCoord' array size cannot " - "be larger than gl_MaxTextureCoords (%u)\n", - state->Const.MaxTextureCoords); - } else if ((size > 0) && (size <= earlier->max_array_access)) { - _mesa_glsl_error(& loc, state, "array size must be > %u due to " - "previous access", - earlier->max_array_access); - } - - earlier->type = var->type; - delete var; - var = NULL; - } else if (state->ARB_fragment_coord_conventions_enable - && strcmp(var->name, "gl_FragCoord") == 0 - && earlier->type == var->type - && earlier->mode == var->mode) { - /* Allow redeclaration of gl_FragCoord for ARB_fcc layout - * qualifiers. - */ - earlier->origin_upper_left = var->origin_upper_left; - earlier->pixel_center_integer = var->pixel_center_integer; - - /* According to section 4.3.7 of the GLSL 1.30 spec, - * the following built-in varaibles can be redeclared with an - * interpolation qualifier: - * * gl_FrontColor - * * gl_BackColor - * * gl_FrontSecondaryColor - * * gl_BackSecondaryColor - * * gl_Color - * * gl_SecondaryColor - */ - } else if (state->language_version >= 130 - && (strcmp(var->name, "gl_FrontColor") == 0 - || strcmp(var->name, "gl_BackColor") == 0 - || strcmp(var->name, "gl_FrontSecondaryColor") == 0 - || strcmp(var->name, "gl_BackSecondaryColor") == 0 - || strcmp(var->name, "gl_Color") == 0 - || strcmp(var->name, "gl_SecondaryColor") == 0) - && earlier->type == var->type - && earlier->mode == var->mode) { - earlier->interpolation = var->interpolation; - - /* Layout qualifiers for gl_FragDepth. */ - } else if (state->AMD_conservative_depth_enable - && strcmp(var->name, "gl_FragDepth") == 0 - && earlier->type == var->type - && earlier->mode == var->mode) { - - /** From the AMD_conservative_depth spec: - * Within any shader, the first redeclarations of gl_FragDepth - * must appear before any use of gl_FragDepth. - */ - if (earlier->used) { - _mesa_glsl_error(&loc, state, - "the first redeclaration of gl_FragDepth " - "must appear before any use of gl_FragDepth"); - } - - /* Prevent inconsistent redeclaration of depth layout qualifier. */ - if (earlier->depth_layout != ir_depth_layout_none - && earlier->depth_layout != var->depth_layout) { - _mesa_glsl_error(&loc, state, - "gl_FragDepth: depth layout is declared here " - "as '%s, but it was previously declared as " - "'%s'", - depth_layout_string(var->depth_layout), - depth_layout_string(earlier->depth_layout)); - } - - earlier->depth_layout = var->depth_layout; - - } else { - _mesa_glsl_error(&loc, state, "`%s' redeclared", decl->identifier); - } - - return earlier; -} - -/** - * Generate the IR for an initializer in a variable declaration - */ -ir_rvalue * -process_initializer(ir_variable *var, ast_declaration *decl, - ast_fully_specified_type *type, - exec_list *initializer_instructions, - struct _mesa_glsl_parse_state *state) -{ - ir_rvalue *result = NULL; - - YYLTYPE initializer_loc = decl->initializer->get_location(); - - /* From page 24 (page 30 of the PDF) of the GLSL 1.10 spec: - * - * "All uniform variables are read-only and are initialized either - * directly by an application via API commands, or indirectly by - * OpenGL." - */ - if ((state->language_version <= 110) - && (var->mode == ir_var_uniform)) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize uniforms in GLSL 1.10"); - } - - if (var->type->is_sampler()) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize samplers"); - } - - if ((var->mode == ir_var_in) && (state->current_function == NULL)) { - _mesa_glsl_error(& initializer_loc, state, - "cannot initialize %s shader input / %s", - _mesa_glsl_shader_target_name(state->target), - (state->target == vertex_shader) - ? "attribute" : "varying"); - } - - ir_dereference *const lhs = new(state) ir_dereference_variable(var); - ir_rvalue *rhs = decl->initializer->hir(initializer_instructions, - state); - - /* Calculate the constant value if this is a const or uniform - * declaration. - */ - if (type->qualifier.flags.q.constant - || type->qualifier.flags.q.uniform) { - ir_rvalue *new_rhs = validate_assignment(state, var->type, rhs, true); - if (new_rhs != NULL) { - rhs = new_rhs; - - ir_constant *constant_value = rhs->constant_expression_value(); - if (!constant_value) { - _mesa_glsl_error(& initializer_loc, state, - "initializer of %s variable `%s' must be a " - "constant expression", - (type->qualifier.flags.q.constant) - ? "const" : "uniform", - decl->identifier); - if (var->type->is_numeric()) { - /* Reduce cascading errors. */ - var->constant_value = ir_constant::zero(state, var->type); - } - } else { - rhs = constant_value; - var->constant_value = constant_value; - } - } else { - _mesa_glsl_error(&initializer_loc, state, - "initializer of type %s cannot be assigned to " - "variable of type %s", - rhs->type->name, var->type->name); - if (var->type->is_numeric()) { - /* Reduce cascading errors. */ - var->constant_value = ir_constant::zero(state, var->type); - } - } - } - - if (rhs && !rhs->type->is_error()) { - bool temp = var->read_only; - if (type->qualifier.flags.q.constant) - var->read_only = false; - - /* Never emit code to initialize a uniform. - */ - const glsl_type *initializer_type; - if (!type->qualifier.flags.q.uniform) { - result = do_assignment(initializer_instructions, state, - lhs, rhs, true, - type->get_location()); - initializer_type = result->type; - } else - initializer_type = rhs->type; - - /* If the declared variable is an unsized array, it must inherrit - * its full type from the initializer. A declaration such as - * - * uniform float a[] = float[](1.0, 2.0, 3.0, 3.0); - * - * becomes - * - * uniform float a[4] = float[](1.0, 2.0, 3.0, 3.0); - * - * The assignment generated in the if-statement (below) will also - * automatically handle this case for non-uniforms. - * - * If the declared variable is not an array, the types must - * already match exactly. As a result, the type assignment - * here can be done unconditionally. For non-uniforms the call - * to do_assignment can change the type of the initializer (via - * the implicit conversion rules). For uniforms the initializer - * must be a constant expression, and the type of that expression - * was validated above. - */ - var->type = initializer_type; - - var->read_only = temp; - } - - return result; -} - -ir_rvalue * -ast_declarator_list::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - const struct glsl_type *decl_type; - const char *type_name = NULL; - ir_rvalue *result = NULL; - YYLTYPE loc = this->get_location(); - - /* From page 46 (page 52 of the PDF) of the GLSL 1.50 spec: - * - * "To ensure that a particular output variable is invariant, it is - * necessary to use the invariant qualifier. It can either be used to - * qualify a previously declared variable as being invariant - * - * invariant gl_Position; // make existing gl_Position be invariant" - * - * In these cases the parser will set the 'invariant' flag in the declarator - * list, and the type will be NULL. - */ - if (this->invariant) { - assert(this->type == NULL); - - if (state->current_function != NULL) { - _mesa_glsl_error(& loc, state, - "All uses of `invariant' keyword must be at global " - "scope\n"); - } - - foreach_list_typed (ast_declaration, decl, link, &this->declarations) { - assert(!decl->is_array); - assert(decl->array_size == NULL); - assert(decl->initializer == NULL); - - ir_variable *const earlier = - state->symbols->get_variable(decl->identifier); - if (earlier == NULL) { - _mesa_glsl_error(& loc, state, - "Undeclared variable `%s' cannot be marked " - "invariant\n", decl->identifier); - } else if ((state->target == vertex_shader) - && (earlier->mode != ir_var_out)) { - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, vertex shader " - "outputs only\n", decl->identifier); - } else if ((state->target == fragment_shader) - && (earlier->mode != ir_var_in)) { - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, fragment shader " - "inputs only\n", decl->identifier); - } else if (earlier->used) { - _mesa_glsl_error(& loc, state, - "variable `%s' may not be redeclared " - "`invariant' after being used", - earlier->name); - } else { - earlier->invariant = true; - } - } - - /* Invariant redeclarations do not have r-values. - */ - return NULL; - } - - assert(this->type != NULL); - assert(!this->invariant); - - /* The type specifier may contain a structure definition. Process that - * before any of the variable declarations. - */ - (void) this->type->specifier->hir(instructions, state); - - decl_type = this->type->specifier->glsl_type(& type_name, state); - if (this->declarations.is_empty()) { - /* The only valid case where the declaration list can be empty is when - * the declaration is setting the default precision of a built-in type - * (e.g., 'precision highp vec4;'). - */ - - if (decl_type != NULL) { - } else { - _mesa_glsl_error(& loc, state, "incomplete declaration"); - } - } - - foreach_list_typed (ast_declaration, decl, link, &this->declarations) { - const struct glsl_type *var_type; - ir_variable *var; - - /* FINISHME: Emit a warning if a variable declaration shadows a - * FINISHME: declaration at a higher scope. - */ - - if ((decl_type == NULL) || decl_type->is_void()) { - if (type_name != NULL) { - _mesa_glsl_error(& loc, state, - "invalid type `%s' in declaration of `%s'", - type_name, decl->identifier); - } else { - _mesa_glsl_error(& loc, state, - "invalid type in declaration of `%s'", - decl->identifier); - } - continue; - } - - if (decl->is_array) { - var_type = process_array_type(&loc, decl_type, decl->array_size, - state); - } else { - var_type = decl_type; - } - - var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto); - - /* From page 22 (page 28 of the PDF) of the GLSL 1.10 specification; - * - * "Global variables can only use the qualifiers const, - * attribute, uni form, or varying. Only one may be - * specified. - * - * Local variables can only use the qualifier const." - * - * This is relaxed in GLSL 1.30. It is also relaxed by any extension - * that adds the 'layout' keyword. - */ - if ((state->language_version < 130) - && !state->ARB_explicit_attrib_location_enable - && !state->ARB_fragment_coord_conventions_enable) { - if (this->type->qualifier.flags.q.out) { - _mesa_glsl_error(& loc, state, - "`out' qualifier in declaration of `%s' " - "only valid for function parameters in %s.", - decl->identifier, state->version_string); - } - if (this->type->qualifier.flags.q.in) { - _mesa_glsl_error(& loc, state, - "`in' qualifier in declaration of `%s' " - "only valid for function parameters in %s.", - decl->identifier, state->version_string); - } - /* FINISHME: Test for other invalid qualifiers. */ - } - - apply_type_qualifier_to_variable(& this->type->qualifier, var, state, - & loc); - - if (this->type->qualifier.flags.q.invariant) { - if ((state->target == vertex_shader) && !(var->mode == ir_var_out || - var->mode == ir_var_inout)) { - /* FINISHME: Note that this doesn't work for invariant on - * a function signature outval - */ - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, vertex shader " - "outputs only\n", var->name); - } else if ((state->target == fragment_shader) && - !(var->mode == ir_var_in || var->mode == ir_var_inout)) { - /* FINISHME: Note that this doesn't work for invariant on - * a function signature inval - */ - _mesa_glsl_error(& loc, state, - "`%s' cannot be marked invariant, fragment shader " - "inputs only\n", var->name); - } - } - - if (state->current_function != NULL) { - const char *mode = NULL; - const char *extra = ""; - - /* There is no need to check for 'inout' here because the parser will - * only allow that in function parameter lists. - */ - if (this->type->qualifier.flags.q.attribute) { - mode = "attribute"; - } else if (this->type->qualifier.flags.q.uniform) { - mode = "uniform"; - } else if (this->type->qualifier.flags.q.varying) { - mode = "varying"; - } else if (this->type->qualifier.flags.q.in) { - mode = "in"; - extra = " or in function parameter list"; - } else if (this->type->qualifier.flags.q.out) { - mode = "out"; - extra = " or in function parameter list"; - } - - if (mode) { - _mesa_glsl_error(& loc, state, - "%s variable `%s' must be declared at " - "global scope%s", - mode, var->name, extra); - } - } else if (var->mode == ir_var_in) { - var->read_only = true; - - if (state->target == vertex_shader) { - bool error_emitted = false; - - /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: - * - * "Vertex shader inputs can only be float, floating-point - * vectors, matrices, signed and unsigned integers and integer - * vectors. Vertex shader inputs can also form arrays of these - * types, but not structures." - * - * From page 31 (page 27 of the PDF) of the GLSL 1.30 spec: - * - * "Vertex shader inputs can only be float, floating-point - * vectors, matrices, signed and unsigned integers and integer - * vectors. They cannot be arrays or structures." - * - * From page 23 (page 29 of the PDF) of the GLSL 1.20 spec: - * - * "The attribute qualifier can be used only with float, - * floating-point vectors, and matrices. Attribute variables - * cannot be declared as arrays or structures." - */ - const glsl_type *check_type = var->type->is_array() - ? var->type->fields.array : var->type; - - switch (check_type->base_type) { - case GLSL_TYPE_FLOAT: - break; - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - if (state->language_version > 120) - break; - /* FALLTHROUGH */ - default: - _mesa_glsl_error(& loc, state, - "vertex shader input / attribute cannot have " - "type %s`%s'", - var->type->is_array() ? "array of " : "", - check_type->name); - error_emitted = true; - } - - if (!error_emitted && (state->language_version <= 130) - && var->type->is_array()) { - _mesa_glsl_error(& loc, state, - "vertex shader input / attribute cannot have " - "array type"); - error_emitted = true; - } - } - } - - /* Integer vertex outputs must be qualified with 'flat'. - * - * From section 4.3.6 of the GLSL 1.30 spec: - * "If a vertex output is a signed or unsigned integer or integer - * vector, then it must be qualified with the interpolation qualifier - * flat." - */ - if (state->language_version >= 130 - && state->target == vertex_shader - && state->current_function == NULL - && var->type->is_integer() - && var->mode == ir_var_out - && var->interpolation != ir_var_flat) { - - _mesa_glsl_error(&loc, state, "If a vertex output is an integer, " - "then it must be qualified with 'flat'"); - } - - - /* Interpolation qualifiers cannot be applied to 'centroid' and - * 'centroid varying'. - * - * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: - * "interpolation qualifiers may only precede the qualifiers in, - * centroid in, out, or centroid out in a declaration. They do not apply - * to the deprecated storage qualifiers varying or centroid varying." - */ - if (state->language_version >= 130 - && this->type->qualifier.has_interpolation() - && this->type->qualifier.flags.q.varying) { - - const char *i = this->type->qualifier.interpolation_string(); - assert(i != NULL); - const char *s; - if (this->type->qualifier.flags.q.centroid) - s = "centroid varying"; - else - s = "varying"; - - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to the " - "deprecated storage qualifier '%s'", i, s); - } - - - /* Interpolation qualifiers can only apply to vertex shader outputs and - * fragment shader inputs. - * - * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec: - * "Outputs from a vertex shader (out) and inputs to a fragment - * shader (in) can be further qualified with one or more of these - * interpolation qualifiers" - */ - if (state->language_version >= 130 - && this->type->qualifier.has_interpolation()) { - - const char *i = this->type->qualifier.interpolation_string(); - assert(i != NULL); - - switch (state->target) { - case vertex_shader: - if (this->type->qualifier.flags.q.in) { - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to vertex " - "shader inputs", i); - } - break; - case fragment_shader: - if (this->type->qualifier.flags.q.out) { - _mesa_glsl_error(&loc, state, - "qualifier '%s' cannot be applied to fragment " - "shader outputs", i); - } - break; - default: - assert(0); - } - } - - - /* From section 4.3.4 of the GLSL 1.30 spec: - * "It is an error to use centroid in in a vertex shader." - */ - if (state->language_version >= 130 - && this->type->qualifier.flags.q.centroid - && this->type->qualifier.flags.q.in - && state->target == vertex_shader) { - - _mesa_glsl_error(&loc, state, - "'centroid in' cannot be used in a vertex shader"); - } - - - /* Precision qualifiers exists only in GLSL versions 1.00 and >= 1.30. - */ - if (this->type->specifier->precision != ast_precision_none - && state->language_version != 100 - && state->language_version < 130) { - - _mesa_glsl_error(&loc, state, - "precision qualifiers are supported only in GLSL ES " - "1.00, and GLSL 1.30 and later"); - } - - - /* Precision qualifiers only apply to floating point and integer types. - * - * From section 4.5.2 of the GLSL 1.30 spec: - * "Any floating point or any integer declaration can have the type - * preceded by one of these precision qualifiers [...] Literal - * constants do not have precision qualifiers. Neither do Boolean - * variables. - */ - if (this->type->specifier->precision != ast_precision_none - && !var->type->is_float() - && !var->type->is_integer() - && !(var->type->is_array() - && (var->type->fields.array->is_float() - || var->type->fields.array->is_integer()))) { - - _mesa_glsl_error(&loc, state, - "precision qualifiers apply only to floating point " - "and integer types"); - } - - /* Process the initializer and add its instructions to a temporary - * list. This list will be added to the instruction stream (below) after - * the declaration is added. This is done because in some cases (such as - * redeclarations) the declaration may not actually be added to the - * instruction stream. - */ - exec_list initializer_instructions; - ir_variable *earlier = get_variable_being_redeclared(var, decl, state); - - if (decl->initializer != NULL) { - result = process_initializer((earlier == NULL) ? var : earlier, - decl, this->type, - &initializer_instructions, state); - } - - /* From page 23 (page 29 of the PDF) of the GLSL 1.10 spec: - * - * "It is an error to write to a const variable outside of - * its declaration, so they must be initialized when - * declared." - */ - if (this->type->qualifier.flags.q.constant && decl->initializer == NULL) { - _mesa_glsl_error(& loc, state, - "const declaration of `%s' must be initialized", - decl->identifier); - } - - /* If the declaration is not a redeclaration, there are a few additional - * semantic checks that must be applied. In addition, variable that was - * created for the declaration should be added to the IR stream. - */ - if (earlier == NULL) { - /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, - * - * "Identifiers starting with "gl_" are reserved for use by - * OpenGL, and may not be declared in a shader as either a - * variable or a function." - */ - if (strncmp(decl->identifier, "gl_", 3) == 0) - _mesa_glsl_error(& loc, state, - "identifier `%s' uses reserved `gl_' prefix", - decl->identifier); - - /* Add the variable to the symbol table. Note that the initializer's - * IR was already processed earlier (though it hasn't been emitted - * yet), without the variable in scope. - * - * This differs from most C-like languages, but it follows the GLSL - * specification. From page 28 (page 34 of the PDF) of the GLSL 1.50 - * spec: - * - * "Within a declaration, the scope of a name starts immediately - * after the initializer if present or immediately after the name - * being declared if not." - */ - if (!state->symbols->add_variable(var)) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, "name `%s' already taken in the " - "current scope", decl->identifier); - continue; - } - - /* Push the variable declaration to the top. It means that all the - * variable declarations will appear in a funny last-to-first order, - * but otherwise we run into trouble if a function is prototyped, a - * global var is decled, then the function is defined with usage of - * the global var. See glslparsertest's CorrectModule.frag. - */ - instructions->push_head(var); - } - - instructions->append_list(&initializer_instructions); - } - - - /* Generally, variable declarations do not have r-values. However, - * one is used for the declaration in - * - * while (bool b = some_condition()) { - * ... - * } - * - * so we return the rvalue from the last seen declaration here. - */ - return result; -} - - -ir_rvalue * -ast_parameter_declarator::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - const struct glsl_type *type; - const char *name = NULL; - YYLTYPE loc = this->get_location(); - - type = this->type->specifier->glsl_type(& name, state); - - if (type == NULL) { - if (name != NULL) { - _mesa_glsl_error(& loc, state, - "invalid type `%s' in declaration of `%s'", - name, this->identifier); - } else { - _mesa_glsl_error(& loc, state, - "invalid type in declaration of `%s'", - this->identifier); - } - - type = glsl_type::error_type; - } - - /* From page 62 (page 68 of the PDF) of the GLSL 1.50 spec: - * - * "Functions that accept no input arguments need not use void in the - * argument list because prototypes (or definitions) are required and - * therefore there is no ambiguity when an empty argument list "( )" is - * declared. The idiom "(void)" as a parameter list is provided for - * convenience." - * - * Placing this check here prevents a void parameter being set up - * for a function, which avoids tripping up checks for main taking - * parameters and lookups of an unnamed symbol. - */ - if (type->is_void()) { - if (this->identifier != NULL) - _mesa_glsl_error(& loc, state, - "named parameter cannot have type `void'"); - - is_void = true; - return NULL; - } - - if (formal_parameter && (this->identifier == NULL)) { - _mesa_glsl_error(& loc, state, "formal parameter lacks a name"); - return NULL; - } - - /* This only handles "vec4 foo[..]". The earlier specifier->glsl_type(...) - * call already handled the "vec4[..] foo" case. - */ - if (this->is_array) { - type = process_array_type(&loc, type, this->array_size, state); - } - - if (type->array_size() == 0) { - _mesa_glsl_error(&loc, state, "arrays passed as parameters must have " - "a declared size."); - type = glsl_type::error_type; - } - - is_void = false; - ir_variable *var = new(ctx) ir_variable(type, this->identifier, ir_var_in); - - /* Apply any specified qualifiers to the parameter declaration. Note that - * for function parameters the default mode is 'in'. - */ - apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc); - - instructions->push_tail(var); - - /* Parameter declarations do not have r-values. - */ - return NULL; -} - - -void -ast_parameter_declarator::parameters_to_hir(exec_list *ast_parameters, - bool formal, - exec_list *ir_parameters, - _mesa_glsl_parse_state *state) -{ - ast_parameter_declarator *void_param = NULL; - unsigned count = 0; - - foreach_list_typed (ast_parameter_declarator, param, link, ast_parameters) { - param->formal_parameter = formal; - param->hir(ir_parameters, state); - - if (param->is_void) - void_param = param; - - count++; - } - - if ((void_param != NULL) && (count > 1)) { - YYLTYPE loc = void_param->get_location(); - - _mesa_glsl_error(& loc, state, - "`void' parameter must be only parameter"); - } -} - - -void -emit_function(_mesa_glsl_parse_state *state, exec_list *instructions, - ir_function *f) -{ - /* Emit the new function header */ - if (state->current_function == NULL) { - instructions->push_tail(f); - } else { - /* IR invariants disallow function declarations or definitions nested - * within other function definitions. Insert the new ir_function - * block in the instruction sequence before the ir_function block - * containing the current ir_function_signature. - */ - ir_function *const curr = - const_cast<ir_function *>(state->current_function->function()); - - curr->insert_before(f); - } -} - - -ir_rvalue * -ast_function::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - ir_function *f = NULL; - ir_function_signature *sig = NULL; - exec_list hir_parameters; - - const char *const name = identifier; - - /* From page 21 (page 27 of the PDF) of the GLSL 1.20 spec, - * - * "Function declarations (prototypes) cannot occur inside of functions; - * they must be at global scope, or for the built-in functions, outside - * the global scope." - * - * From page 27 (page 33 of the PDF) of the GLSL ES 1.00.16 spec, - * - * "User defined functions may only be defined within the global scope." - * - * Note that this language does not appear in GLSL 1.10. - */ - if ((state->current_function != NULL) && (state->language_version != 110)) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "declaration of function `%s' not allowed within " - "function body", name); - } - - /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec, - * - * "Identifiers starting with "gl_" are reserved for use by - * OpenGL, and may not be declared in a shader as either a - * variable or a function." - */ - if (strncmp(name, "gl_", 3) == 0) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "identifier `%s' uses reserved `gl_' prefix", name); - } - - /* Convert the list of function parameters to HIR now so that they can be - * used below to compare this function's signature with previously seen - * signatures for functions with the same name. - */ - ast_parameter_declarator::parameters_to_hir(& this->parameters, - is_definition, - & hir_parameters, state); - - const char *return_type_name; - const glsl_type *return_type = - this->return_type->specifier->glsl_type(& return_type_name, state); - - if (!return_type) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, - "function `%s' has undeclared return type `%s'", - name, return_type_name); - return_type = glsl_type::error_type; - } - - /* From page 56 (page 62 of the PDF) of the GLSL 1.30 spec: - * "No qualifier is allowed on the return type of a function." - */ - if (this->return_type->has_qualifiers()) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(& loc, state, - "function `%s' return type has qualifiers", name); - } - - /* Verify that this function's signature either doesn't match a previously - * seen signature for a function with the same name, or, if a match is found, - * that the previously seen signature does not have an associated definition. - */ - f = state->symbols->get_function(name); - if (f != NULL && (state->es_shader || f->has_user_signature())) { - sig = f->exact_matching_signature(&hir_parameters); - if (sig != NULL) { - const char *badvar = sig->qualifiers_match(&hir_parameters); - if (badvar != NULL) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function `%s' parameter `%s' " - "qualifiers don't match prototype", name, badvar); - } - - if (sig->return_type != return_type) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function `%s' return type doesn't " - "match prototype", name); - } - - if (is_definition && sig->is_defined) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "function `%s' redefined", name); - } - } - } else { - f = new(ctx) ir_function(name); - if (!state->symbols->add_function(f)) { - /* This function name shadows a non-function use of the same name. */ - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(&loc, state, "function name `%s' conflicts with " - "non-function", name); - return NULL; - } - - emit_function(state, instructions, f); - } - - /* Verify the return type of main() */ - if (strcmp(name, "main") == 0) { - if (! return_type->is_void()) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "main() must return void"); - } - - if (!hir_parameters.is_empty()) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "main() must not take any parameters"); - } - } - - /* Finish storing the information about this new function in its signature. - */ - if (sig == NULL) { - sig = new(ctx) ir_function_signature(return_type); - f->add_signature(sig); - } - - sig->replace_parameters(&hir_parameters); - signature = sig; - - /* Function declarations (prototypes) do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_function_definition::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - prototype->is_definition = true; - prototype->hir(instructions, state); - - ir_function_signature *signature = prototype->signature; - if (signature == NULL) - return NULL; - - assert(state->current_function == NULL); - state->current_function = signature; - state->found_return = false; - - /* Duplicate parameters declared in the prototype as concrete variables. - * Add these to the symbol table. - */ - state->symbols->push_scope(); - foreach_iter(exec_list_iterator, iter, signature->parameters) { - ir_variable *const var = ((ir_instruction *) iter.get())->as_variable(); - - assert(var != NULL); - - /* The only way a parameter would "exist" is if two parameters have - * the same name. - */ - if (state->symbols->name_declared_this_scope(var->name)) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, "parameter `%s' redeclared", var->name); - } else { - state->symbols->add_variable(var); - } - } - - /* Convert the body of the function to HIR. */ - this->body->hir(&signature->body, state); - signature->is_defined = true; - - state->symbols->pop_scope(); - - assert(state->current_function == signature); - state->current_function = NULL; - - if (!signature->return_type->is_void() && !state->found_return) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(& loc, state, "function `%s' has non-void return type " - "%s, but no return statement", - signature->function_name(), - signature->return_type->name); - } - - /* Function definitions do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_jump_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - switch (mode) { - case ast_return: { - ir_return *inst; - assert(state->current_function); - - if (opt_return_value) { - ir_rvalue *const ret = opt_return_value->hir(instructions, state); - - /* The value of the return type can be NULL if the shader says - * 'return foo();' and foo() is a function that returns void. - * - * NOTE: The GLSL spec doesn't say that this is an error. The type - * of the return value is void. If the return type of the function is - * also void, then this should compile without error. Seriously. - */ - const glsl_type *const ret_type = - (ret == NULL) ? glsl_type::void_type : ret->type; - - /* Implicit conversions are not allowed for return values. */ - if (state->current_function->return_type != ret_type) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`return' with wrong type %s, in function `%s' " - "returning %s", - ret_type->name, - state->current_function->function_name(), - state->current_function->return_type->name); - } - - inst = new(ctx) ir_return(ret); - } else { - if (state->current_function->return_type->base_type != - GLSL_TYPE_VOID) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`return' with no value, in function %s returning " - "non-void", - state->current_function->function_name()); - } - inst = new(ctx) ir_return; - } - - state->found_return = true; - instructions->push_tail(inst); - break; - } - - case ast_discard: - if (state->target != fragment_shader) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`discard' may only appear in a fragment shader"); - } - instructions->push_tail(new(ctx) ir_discard); - break; - - case ast_break: - case ast_continue: - /* FINISHME: Handle switch-statements. They cannot contain 'continue', - * FINISHME: and they use a different IR instruction for 'break'. - */ - /* FINISHME: Correctly handle the nesting. If a switch-statement is - * FINISHME: inside a loop, a 'continue' is valid and will bind to the - * FINISHME: loop. - */ - if (state->loop_or_switch_nesting == NULL) { - YYLTYPE loc = this->get_location(); - - _mesa_glsl_error(& loc, state, - "`%s' may only appear in a loop", - (mode == ast_break) ? "break" : "continue"); - } else { - ir_loop *const loop = state->loop_or_switch_nesting->as_loop(); - - /* Inline the for loop expression again, since we don't know - * where near the end of the loop body the normal copy of it - * is going to be placed. - */ - if (mode == ast_continue && - state->loop_or_switch_nesting_ast->rest_expression) { - state->loop_or_switch_nesting_ast->rest_expression->hir(instructions, - state); - } - - if (loop != NULL) { - ir_loop_jump *const jump = - new(ctx) ir_loop_jump((mode == ast_break) - ? ir_loop_jump::jump_break - : ir_loop_jump::jump_continue); - instructions->push_tail(jump); - } - } - - break; - } - - /* Jump instructions do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_selection_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - ir_rvalue *const condition = this->condition->hir(instructions, state); - - /* From page 66 (page 72 of the PDF) of the GLSL 1.50 spec: - * - * "Any expression whose type evaluates to a Boolean can be used as the - * conditional expression bool-expression. Vector types are not accepted - * as the expression to if." - * - * The checks are separated so that higher quality diagnostics can be - * generated for cases where both rules are violated. - */ - if (!condition->type->is_boolean() || !condition->type->is_scalar()) { - YYLTYPE loc = this->condition->get_location(); - - _mesa_glsl_error(& loc, state, "if-statement condition must be scalar " - "boolean"); - } - - ir_if *const stmt = new(ctx) ir_if(condition); - - if (then_statement != NULL) { - state->symbols->push_scope(); - then_statement->hir(& stmt->then_instructions, state); - state->symbols->pop_scope(); - } - - if (else_statement != NULL) { - state->symbols->push_scope(); - else_statement->hir(& stmt->else_instructions, state); - state->symbols->pop_scope(); - } - - instructions->push_tail(stmt); - - /* if-statements do not have r-values. - */ - return NULL; -} - - -void -ast_iteration_statement::condition_to_hir(ir_loop *stmt, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - if (condition != NULL) { - ir_rvalue *const cond = - condition->hir(& stmt->body_instructions, state); - - if ((cond == NULL) - || !cond->type->is_boolean() || !cond->type->is_scalar()) { - YYLTYPE loc = condition->get_location(); - - _mesa_glsl_error(& loc, state, - "loop condition must be scalar boolean"); - } else { - /* As the first code in the loop body, generate a block that looks - * like 'if (!condition) break;' as the loop termination condition. - */ - ir_rvalue *const not_cond = - new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond, - NULL); - - ir_if *const if_stmt = new(ctx) ir_if(not_cond); - - ir_jump *const break_stmt = - new(ctx) ir_loop_jump(ir_loop_jump::jump_break); - - if_stmt->then_instructions.push_tail(break_stmt); - stmt->body_instructions.push_tail(if_stmt); - } - } -} - - -ir_rvalue * -ast_iteration_statement::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - - /* For-loops and while-loops start a new scope, but do-while loops do not. - */ - if (mode != ast_do_while) - state->symbols->push_scope(); - - if (init_statement != NULL) - init_statement->hir(instructions, state); - - ir_loop *const stmt = new(ctx) ir_loop(); - instructions->push_tail(stmt); - - /* Track the current loop and / or switch-statement nesting. - */ - ir_instruction *const nesting = state->loop_or_switch_nesting; - ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast; - - state->loop_or_switch_nesting = stmt; - state->loop_or_switch_nesting_ast = this; - - if (mode != ast_do_while) - condition_to_hir(stmt, state); - - if (body != NULL) - body->hir(& stmt->body_instructions, state); - - if (rest_expression != NULL) - rest_expression->hir(& stmt->body_instructions, state); - - if (mode == ast_do_while) - condition_to_hir(stmt, state); - - if (mode != ast_do_while) - state->symbols->pop_scope(); - - /* Restore previous nesting before returning. - */ - state->loop_or_switch_nesting = nesting; - state->loop_or_switch_nesting_ast = nesting_ast; - - /* Loops do not have r-values. - */ - return NULL; -} - - -ir_rvalue * -ast_type_specifier::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - if (!this->is_precision_statement && this->structure == NULL) - return NULL; - - YYLTYPE loc = this->get_location(); - - if (this->precision != ast_precision_none - && state->language_version != 100 - && state->language_version < 130) { - _mesa_glsl_error(&loc, state, - "precision qualifiers exist only in " - "GLSL ES 1.00, and GLSL 1.30 and later"); - return NULL; - } - if (this->precision != ast_precision_none - && this->structure != NULL) { - _mesa_glsl_error(&loc, state, - "precision qualifiers do not apply to structures"); - return NULL; - } - - /* If this is a precision statement, check that the type to which it is - * applied is either float or int. - * - * From section 4.5.3 of the GLSL 1.30 spec: - * "The precision statement - * precision precision-qualifier type; - * can be used to establish a default precision qualifier. The type - * field can be either int or float [...]. Any other types or - * qualifiers will result in an error. - */ - if (this->is_precision_statement) { - assert(this->precision != ast_precision_none); - assert(this->structure == NULL); /* The check for structures was - * performed above. */ - if (this->is_array) { - _mesa_glsl_error(&loc, state, - "default precision statements do not apply to " - "arrays"); - return NULL; - } - if (this->type_specifier != ast_float - && this->type_specifier != ast_int) { - _mesa_glsl_error(&loc, state, - "default precision statements apply only to types " - "float and int"); - return NULL; - } - - /* FINISHME: Translate precision statements into IR. */ - return NULL; - } - - if (this->structure != NULL) - return this->structure->hir(instructions, state); - - return NULL; -} - - -ir_rvalue * -ast_struct_specifier::hir(exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - unsigned decl_count = 0; - - /* Make an initial pass over the list of structure fields to determine how - * many there are. Each element in this list is an ast_declarator_list. - * This means that we actually need to count the number of elements in the - * 'declarations' list in each of the elements. - */ - foreach_list_typed (ast_declarator_list, decl_list, link, - &this->declarations) { - foreach_list_const (decl_ptr, & decl_list->declarations) { - decl_count++; - } - } - - /* Allocate storage for the structure fields and process the field - * declarations. As the declarations are processed, try to also convert - * the types to HIR. This ensures that structure definitions embedded in - * other structure definitions are processed. - */ - glsl_struct_field *const fields = ralloc_array(state, glsl_struct_field, - decl_count); - - unsigned i = 0; - foreach_list_typed (ast_declarator_list, decl_list, link, - &this->declarations) { - const char *type_name; - - decl_list->type->specifier->hir(instructions, state); - - /* Section 10.9 of the GLSL ES 1.00 specification states that - * embedded structure definitions have been removed from the language. - */ - if (state->es_shader && decl_list->type->specifier->structure != NULL) { - YYLTYPE loc = this->get_location(); - _mesa_glsl_error(&loc, state, "Embedded structure definitions are " - "not allowed in GLSL ES 1.00."); - } - - const glsl_type *decl_type = - decl_list->type->specifier->glsl_type(& type_name, state); - - foreach_list_typed (ast_declaration, decl, link, - &decl_list->declarations) { - const struct glsl_type *field_type = decl_type; - if (decl->is_array) { - YYLTYPE loc = decl->get_location(); - field_type = process_array_type(&loc, decl_type, decl->array_size, - state); - } - fields[i].type = (field_type != NULL) - ? field_type : glsl_type::error_type; - fields[i].name = decl->identifier; - i++; - } - } - - assert(i == decl_count); - - const glsl_type *t = - glsl_type::get_record_instance(fields, decl_count, this->name); - - YYLTYPE loc = this->get_location(); - if (!state->symbols->add_type(name, t)) { - _mesa_glsl_error(& loc, state, "struct `%s' previously defined", name); - } else { - const glsl_type **s = reralloc(state, state->user_structures, - const glsl_type *, - state->num_user_structures + 1); - if (s != NULL) { - s[state->num_user_structures] = t; - state->user_structures = s; - state->num_user_structures++; - } - } - - /* Structure type definitions do not have r-values. - */ - return NULL; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ast_to_hir.c
+ * Convert abstract syntax to to high-level intermediate reprensentation (HIR).
+ *
+ * During the conversion to HIR, the majority of the symantic checking is
+ * preformed on the program. This includes:
+ *
+ * * Symbol table management
+ * * Type checking
+ * * Function binding
+ *
+ * The majority of this work could be done during parsing, and the parser could
+ * probably generate HIR directly. However, this results in frequent changes
+ * to the parser code. Since we do not assume that every system this complier
+ * is built on will have Flex and Bison installed, we have to store the code
+ * generated by these tools in our version control system. In other parts of
+ * the system we've seen problems where a parser was changed but the generated
+ * code was not committed, merge conflicts where created because two developers
+ * had slightly different versions of Bison installed, etc.
+ *
+ * I have also noticed that running Bison generated parsers in GDB is very
+ * irritating. When you get a segfault on '$$ = $1->foo', you can't very
+ * well 'print $1' in GDB.
+ *
+ * As a result, my preference is to put as little C code as possible in the
+ * parser (and lexer) sources.
+ */
+
+#include "main/core.h" /* for struct gl_extensions */
+#include "glsl_symbol_table.h"
+#include "glsl_parser_extras.h"
+#include "ast.h"
+#include "glsl_types.h"
+#include "ir.h"
+
+void
+_mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state)
+{
+ _mesa_glsl_initialize_variables(instructions, state);
+ _mesa_glsl_initialize_functions(state);
+
+ state->symbols->language_version = state->language_version;
+
+ state->current_function = NULL;
+
+ /* Section 4.2 of the GLSL 1.20 specification states:
+ * "The built-in functions are scoped in a scope outside the global scope
+ * users declare global variables in. That is, a shader's global scope,
+ * available for user-defined functions and global variables, is nested
+ * inside the scope containing the built-in functions."
+ *
+ * Since built-in functions like ftransform() access built-in variables,
+ * it follows that those must be in the outer scope as well.
+ *
+ * We push scope here to create this nesting effect...but don't pop.
+ * This way, a shader's globals are still in the symbol table for use
+ * by the linker.
+ */
+ state->symbols->push_scope();
+
+ foreach_list_typed (ast_node, ast, link, & state->translation_unit)
+ ast->hir(instructions, state);
+}
+
+
+/**
+ * If a conversion is available, convert one operand to a different type
+ *
+ * The \c from \c ir_rvalue is converted "in place".
+ *
+ * \param to Type that the operand it to be converted to
+ * \param from Operand that is being converted
+ * \param state GLSL compiler state
+ *
+ * \return
+ * If a conversion is possible (or unnecessary), \c true is returned.
+ * Otherwise \c false is returned.
+ */
+bool
+apply_implicit_conversion(const glsl_type *to, ir_rvalue * &from,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ if (to->base_type == from->type->base_type)
+ return true;
+
+ /* This conversion was added in GLSL 1.20. If the compilation mode is
+ * GLSL 1.10, the conversion is skipped.
+ */
+ if (state->language_version < 120)
+ return false;
+
+ /* From page 27 (page 33 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "There are no implicit array or structure conversions. For
+ * example, an array of int cannot be implicitly converted to an
+ * array of float. There are no implicit conversions between
+ * signed and unsigned integers."
+ */
+ /* FINISHME: The above comment is partially a lie. There is int/uint
+ * FINISHME: conversion for immediate constants.
+ */
+ if (!to->is_float() || !from->type->is_numeric())
+ return false;
+
+ /* Convert to a floating point type with the same number of components
+ * as the original type - i.e. int to float, not int to vec4.
+ */
+ to = glsl_type::get_instance(GLSL_TYPE_FLOAT, from->type->vector_elements,
+ from->type->matrix_columns);
+
+ switch (from->type->base_type) {
+ case GLSL_TYPE_INT:
+ from = new(ctx) ir_expression(ir_unop_i2f, to, from, NULL);
+ break;
+ case GLSL_TYPE_UINT:
+ from = new(ctx) ir_expression(ir_unop_u2f, to, from, NULL);
+ break;
+ case GLSL_TYPE_BOOL:
+ from = new(ctx) ir_expression(ir_unop_b2f, to, from, NULL);
+ break;
+ default:
+ assert(0);
+ }
+
+ return true;
+}
+
+
+static const struct glsl_type *
+arithmetic_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b,
+ bool multiply,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ const glsl_type *type_a = value_a->type;
+ const glsl_type *type_b = value_b->type;
+
+ /* From GLSL 1.50 spec, page 56:
+ *
+ * "The arithmetic binary operators add (+), subtract (-),
+ * multiply (*), and divide (/) operate on integer and
+ * floating-point scalars, vectors, and matrices."
+ */
+ if (!type_a->is_numeric() || !type_b->is_numeric()) {
+ _mesa_glsl_error(loc, state,
+ "Operands to arithmetic operators must be numeric");
+ return glsl_type::error_type;
+ }
+
+
+ /* "If one operand is floating-point based and the other is
+ * not, then the conversions from Section 4.1.10 "Implicit
+ * Conversions" are applied to the non-floating-point-based operand."
+ */
+ if (!apply_implicit_conversion(type_a, value_b, state)
+ && !apply_implicit_conversion(type_b, value_a, state)) {
+ _mesa_glsl_error(loc, state,
+ "Could not implicitly convert operands to "
+ "arithmetic operator");
+ return glsl_type::error_type;
+ }
+ type_a = value_a->type;
+ type_b = value_b->type;
+
+ /* "If the operands are integer types, they must both be signed or
+ * both be unsigned."
+ *
+ * From this rule and the preceeding conversion it can be inferred that
+ * both types must be GLSL_TYPE_FLOAT, or GLSL_TYPE_UINT, or GLSL_TYPE_INT.
+ * The is_numeric check above already filtered out the case where either
+ * type is not one of these, so now the base types need only be tested for
+ * equality.
+ */
+ if (type_a->base_type != type_b->base_type) {
+ _mesa_glsl_error(loc, state,
+ "base type mismatch for arithmetic operator");
+ return glsl_type::error_type;
+ }
+
+ /* "All arithmetic binary operators result in the same fundamental type
+ * (signed integer, unsigned integer, or floating-point) as the
+ * operands they operate on, after operand type conversion. After
+ * conversion, the following cases are valid
+ *
+ * * The two operands are scalars. In this case the operation is
+ * applied, resulting in a scalar."
+ */
+ if (type_a->is_scalar() && type_b->is_scalar())
+ return type_a;
+
+ /* "* One operand is a scalar, and the other is a vector or matrix.
+ * In this case, the scalar operation is applied independently to each
+ * component of the vector or matrix, resulting in the same size
+ * vector or matrix."
+ */
+ if (type_a->is_scalar()) {
+ if (!type_b->is_scalar())
+ return type_b;
+ } else if (type_b->is_scalar()) {
+ return type_a;
+ }
+
+ /* All of the combinations of <scalar, scalar>, <vector, scalar>,
+ * <scalar, vector>, <scalar, matrix>, and <matrix, scalar> have been
+ * handled.
+ */
+ assert(!type_a->is_scalar());
+ assert(!type_b->is_scalar());
+
+ /* "* The two operands are vectors of the same size. In this case, the
+ * operation is done component-wise resulting in the same size
+ * vector."
+ */
+ if (type_a->is_vector() && type_b->is_vector()) {
+ if (type_a == type_b) {
+ return type_a;
+ } else {
+ _mesa_glsl_error(loc, state,
+ "vector size mismatch for arithmetic operator");
+ return glsl_type::error_type;
+ }
+ }
+
+ /* All of the combinations of <scalar, scalar>, <vector, scalar>,
+ * <scalar, vector>, <scalar, matrix>, <matrix, scalar>, and
+ * <vector, vector> have been handled. At least one of the operands must
+ * be matrix. Further, since there are no integer matrix types, the base
+ * type of both operands must be float.
+ */
+ assert(type_a->is_matrix() || type_b->is_matrix());
+ assert(type_a->base_type == GLSL_TYPE_FLOAT);
+ assert(type_b->base_type == GLSL_TYPE_FLOAT);
+
+ /* "* The operator is add (+), subtract (-), or divide (/), and the
+ * operands are matrices with the same number of rows and the same
+ * number of columns. In this case, the operation is done component-
+ * wise resulting in the same size matrix."
+ * * The operator is multiply (*), where both operands are matrices or
+ * one operand is a vector and the other a matrix. A right vector
+ * operand is treated as a column vector and a left vector operand as a
+ * row vector. In all these cases, it is required that the number of
+ * columns of the left operand is equal to the number of rows of the
+ * right operand. Then, the multiply (*) operation does a linear
+ * algebraic multiply, yielding an object that has the same number of
+ * rows as the left operand and the same number of columns as the right
+ * operand. Section 5.10 "Vector and Matrix Operations" explains in
+ * more detail how vectors and matrices are operated on."
+ */
+ if (! multiply) {
+ if (type_a == type_b)
+ return type_a;
+ } else {
+ if (type_a->is_matrix() && type_b->is_matrix()) {
+ /* Matrix multiply. The columns of A must match the rows of B. Given
+ * the other previously tested constraints, this means the vector type
+ * of a row from A must be the same as the vector type of a column from
+ * B.
+ */
+ if (type_a->row_type() == type_b->column_type()) {
+ /* The resulting matrix has the number of columns of matrix B and
+ * the number of rows of matrix A. We get the row count of A by
+ * looking at the size of a vector that makes up a column. The
+ * transpose (size of a row) is done for B.
+ */
+ const glsl_type *const type =
+ glsl_type::get_instance(type_a->base_type,
+ type_a->column_type()->vector_elements,
+ type_b->row_type()->vector_elements);
+ assert(type != glsl_type::error_type);
+
+ return type;
+ }
+ } else if (type_a->is_matrix()) {
+ /* A is a matrix and B is a column vector. Columns of A must match
+ * rows of B. Given the other previously tested constraints, this
+ * means the vector type of a row from A must be the same as the
+ * vector the type of B.
+ */
+ if (type_a->row_type() == type_b) {
+ /* The resulting vector has a number of elements equal to
+ * the number of rows of matrix A. */
+ const glsl_type *const type =
+ glsl_type::get_instance(type_a->base_type,
+ type_a->column_type()->vector_elements,
+ 1);
+ assert(type != glsl_type::error_type);
+
+ return type;
+ }
+ } else {
+ assert(type_b->is_matrix());
+
+ /* A is a row vector and B is a matrix. Columns of A must match rows
+ * of B. Given the other previously tested constraints, this means
+ * the type of A must be the same as the vector type of a column from
+ * B.
+ */
+ if (type_a == type_b->column_type()) {
+ /* The resulting vector has a number of elements equal to
+ * the number of columns of matrix B. */
+ const glsl_type *const type =
+ glsl_type::get_instance(type_a->base_type,
+ type_b->row_type()->vector_elements,
+ 1);
+ assert(type != glsl_type::error_type);
+
+ return type;
+ }
+ }
+
+ _mesa_glsl_error(loc, state, "size mismatch for matrix multiplication");
+ return glsl_type::error_type;
+ }
+
+
+ /* "All other cases are illegal."
+ */
+ _mesa_glsl_error(loc, state, "type mismatch");
+ return glsl_type::error_type;
+}
+
+
+static const struct glsl_type *
+unary_arithmetic_result_type(const struct glsl_type *type,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ /* From GLSL 1.50 spec, page 57:
+ *
+ * "The arithmetic unary operators negate (-), post- and pre-increment
+ * and decrement (-- and ++) operate on integer or floating-point
+ * values (including vectors and matrices). All unary operators work
+ * component-wise on their operands. These result with the same type
+ * they operated on."
+ */
+ if (!type->is_numeric()) {
+ _mesa_glsl_error(loc, state,
+ "Operands to arithmetic operators must be numeric");
+ return glsl_type::error_type;
+ }
+
+ return type;
+}
+
+/**
+ * \brief Return the result type of a bit-logic operation.
+ *
+ * If the given types to the bit-logic operator are invalid, return
+ * glsl_type::error_type.
+ *
+ * \param type_a Type of LHS of bit-logic op
+ * \param type_b Type of RHS of bit-logic op
+ */
+static const struct glsl_type *
+bit_logic_result_type(const struct glsl_type *type_a,
+ const struct glsl_type *type_b,
+ ast_operators op,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ if (state->language_version < 130) {
+ _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30");
+ return glsl_type::error_type;
+ }
+
+ /* From page 50 (page 56 of PDF) of GLSL 1.30 spec:
+ *
+ * "The bitwise operators and (&), exclusive-or (^), and inclusive-or
+ * (|). The operands must be of type signed or unsigned integers or
+ * integer vectors."
+ */
+ if (!type_a->is_integer()) {
+ _mesa_glsl_error(loc, state, "LHS of `%s' must be an integer",
+ ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+ if (!type_b->is_integer()) {
+ _mesa_glsl_error(loc, state, "RHS of `%s' must be an integer",
+ ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* "The fundamental types of the operands (signed or unsigned) must
+ * match,"
+ */
+ if (type_a->base_type != type_b->base_type) {
+ _mesa_glsl_error(loc, state, "operands of `%s' must have the same "
+ "base type", ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* "The operands cannot be vectors of differing size." */
+ if (type_a->is_vector() &&
+ type_b->is_vector() &&
+ type_a->vector_elements != type_b->vector_elements) {
+ _mesa_glsl_error(loc, state, "operands of `%s' cannot be vectors of "
+ "different sizes", ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* "If one operand is a scalar and the other a vector, the scalar is
+ * applied component-wise to the vector, resulting in the same type as
+ * the vector. The fundamental types of the operands [...] will be the
+ * resulting fundamental type."
+ */
+ if (type_a->is_scalar())
+ return type_b;
+ else
+ return type_a;
+}
+
+static const struct glsl_type *
+modulus_result_type(const struct glsl_type *type_a,
+ const struct glsl_type *type_b,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ if (state->language_version < 130) {
+ _mesa_glsl_error(loc, state,
+ "operator '%%' is reserved in %s",
+ state->version_string);
+ return glsl_type::error_type;
+ }
+
+ /* From GLSL 1.50 spec, page 56:
+ * "The operator modulus (%) operates on signed or unsigned integers or
+ * integer vectors. The operand types must both be signed or both be
+ * unsigned."
+ */
+ if (!type_a->is_integer() || !type_b->is_integer()
+ || (type_a->base_type != type_b->base_type)) {
+ _mesa_glsl_error(loc, state, "type mismatch");
+ return glsl_type::error_type;
+ }
+
+ /* "The operands cannot be vectors of differing size. If one operand is
+ * a scalar and the other vector, then the scalar is applied component-
+ * wise to the vector, resulting in the same type as the vector. If both
+ * are vectors of the same size, the result is computed component-wise."
+ */
+ if (type_a->is_vector()) {
+ if (!type_b->is_vector()
+ || (type_a->vector_elements == type_b->vector_elements))
+ return type_a;
+ } else
+ return type_b;
+
+ /* "The operator modulus (%) is not defined for any other data types
+ * (non-integer types)."
+ */
+ _mesa_glsl_error(loc, state, "type mismatch");
+ return glsl_type::error_type;
+}
+
+
+static const struct glsl_type *
+relational_result_type(ir_rvalue * &value_a, ir_rvalue * &value_b,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ const glsl_type *type_a = value_a->type;
+ const glsl_type *type_b = value_b->type;
+
+ /* From GLSL 1.50 spec, page 56:
+ * "The relational operators greater than (>), less than (<), greater
+ * than or equal (>=), and less than or equal (<=) operate only on
+ * scalar integer and scalar floating-point expressions."
+ */
+ if (!type_a->is_numeric()
+ || !type_b->is_numeric()
+ || !type_a->is_scalar()
+ || !type_b->is_scalar()) {
+ _mesa_glsl_error(loc, state,
+ "Operands to relational operators must be scalar and "
+ "numeric");
+ return glsl_type::error_type;
+ }
+
+ /* "Either the operands' types must match, or the conversions from
+ * Section 4.1.10 "Implicit Conversions" will be applied to the integer
+ * operand, after which the types must match."
+ */
+ if (!apply_implicit_conversion(type_a, value_b, state)
+ && !apply_implicit_conversion(type_b, value_a, state)) {
+ _mesa_glsl_error(loc, state,
+ "Could not implicitly convert operands to "
+ "relational operator");
+ return glsl_type::error_type;
+ }
+ type_a = value_a->type;
+ type_b = value_b->type;
+
+ if (type_a->base_type != type_b->base_type) {
+ _mesa_glsl_error(loc, state, "base type mismatch");
+ return glsl_type::error_type;
+ }
+
+ /* "The result is scalar Boolean."
+ */
+ return glsl_type::bool_type;
+}
+
+/**
+ * \brief Return the result type of a bit-shift operation.
+ *
+ * If the given types to the bit-shift operator are invalid, return
+ * glsl_type::error_type.
+ *
+ * \param type_a Type of LHS of bit-shift op
+ * \param type_b Type of RHS of bit-shift op
+ */
+static const struct glsl_type *
+shift_result_type(const struct glsl_type *type_a,
+ const struct glsl_type *type_b,
+ ast_operators op,
+ struct _mesa_glsl_parse_state *state, YYLTYPE *loc)
+{
+ if (state->language_version < 130) {
+ _mesa_glsl_error(loc, state, "bit operations require GLSL 1.30");
+ return glsl_type::error_type;
+ }
+
+ /* From page 50 (page 56 of the PDF) of the GLSL 1.30 spec:
+ *
+ * "The shift operators (<<) and (>>). For both operators, the operands
+ * must be signed or unsigned integers or integer vectors. One operand
+ * can be signed while the other is unsigned."
+ */
+ if (!type_a->is_integer()) {
+ _mesa_glsl_error(loc, state, "LHS of operator %s must be an integer or "
+ "integer vector", ast_expression::operator_string(op));
+ return glsl_type::error_type;
+
+ }
+ if (!type_b->is_integer()) {
+ _mesa_glsl_error(loc, state, "RHS of operator %s must be an integer or "
+ "integer vector", ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* "If the first operand is a scalar, the second operand has to be
+ * a scalar as well."
+ */
+ if (type_a->is_scalar() && !type_b->is_scalar()) {
+ _mesa_glsl_error(loc, state, "If the first operand of %s is scalar, the "
+ "second must be scalar as well",
+ ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* If both operands are vectors, check that they have same number of
+ * elements.
+ */
+ if (type_a->is_vector() &&
+ type_b->is_vector() &&
+ type_a->vector_elements != type_b->vector_elements) {
+ _mesa_glsl_error(loc, state, "Vector operands to operator %s must "
+ "have same number of elements",
+ ast_expression::operator_string(op));
+ return glsl_type::error_type;
+ }
+
+ /* "In all cases, the resulting type will be the same type as the left
+ * operand."
+ */
+ return type_a;
+}
+
+/**
+ * Validates that a value can be assigned to a location with a specified type
+ *
+ * Validates that \c rhs can be assigned to some location. If the types are
+ * not an exact match but an automatic conversion is possible, \c rhs will be
+ * converted.
+ *
+ * \return
+ * \c NULL if \c rhs cannot be assigned to a location with type \c lhs_type.
+ * Otherwise the actual RHS to be assigned will be returned. This may be
+ * \c rhs, or it may be \c rhs after some type conversion.
+ *
+ * \note
+ * In addition to being used for assignments, this function is used to
+ * type-check return values.
+ */
+ir_rvalue *
+validate_assignment(struct _mesa_glsl_parse_state *state,
+ const glsl_type *lhs_type, ir_rvalue *rhs,
+ bool is_initializer)
+{
+ /* If there is already some error in the RHS, just return it. Anything
+ * else will lead to an avalanche of error message back to the user.
+ */
+ if (rhs->type->is_error())
+ return rhs;
+
+ /* If the types are identical, the assignment can trivially proceed.
+ */
+ if (rhs->type == lhs_type)
+ return rhs;
+
+ /* If the array element types are the same and the size of the LHS is zero,
+ * the assignment is okay for initializers embedded in variable
+ * declarations.
+ *
+ * Note: Whole-array assignments are not permitted in GLSL 1.10, but this
+ * is handled by ir_dereference::is_lvalue.
+ */
+ if (is_initializer && lhs_type->is_array() && rhs->type->is_array()
+ && (lhs_type->element_type() == rhs->type->element_type())
+ && (lhs_type->array_size() == 0)) {
+ return rhs;
+ }
+
+ /* Check for implicit conversion in GLSL 1.20 */
+ if (apply_implicit_conversion(lhs_type, rhs, state)) {
+ if (rhs->type == lhs_type)
+ return rhs;
+ }
+
+ return NULL;
+}
+
+ir_rvalue *
+do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state,
+ ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer,
+ YYLTYPE lhs_loc)
+{
+ void *ctx = state;
+ bool error_emitted = (lhs->type->is_error() || rhs->type->is_error());
+
+ if (!error_emitted) {
+ if (lhs->variable_referenced() != NULL
+ && lhs->variable_referenced()->read_only) {
+ _mesa_glsl_error(&lhs_loc, state,
+ "assignment to read-only variable '%s'",
+ lhs->variable_referenced()->name);
+ error_emitted = true;
+
+ } else if (!lhs->is_lvalue()) {
+ _mesa_glsl_error(& lhs_loc, state, "non-lvalue in assignment");
+ error_emitted = true;
+ }
+
+ if (state->es_shader && lhs->type->is_array()) {
+ _mesa_glsl_error(&lhs_loc, state, "whole array assignment is not "
+ "allowed in GLSL ES 1.00.");
+ error_emitted = true;
+ }
+ }
+
+ ir_rvalue *new_rhs =
+ validate_assignment(state, lhs->type, rhs, is_initializer);
+ if (new_rhs == NULL) {
+ _mesa_glsl_error(& lhs_loc, state, "type mismatch");
+ } else {
+ rhs = new_rhs;
+
+ /* If the LHS array was not declared with a size, it takes it size from
+ * the RHS. If the LHS is an l-value and a whole array, it must be a
+ * dereference of a variable. Any other case would require that the LHS
+ * is either not an l-value or not a whole array.
+ */
+ if (lhs->type->array_size() == 0) {
+ ir_dereference *const d = lhs->as_dereference();
+
+ assert(d != NULL);
+
+ ir_variable *const var = d->variable_referenced();
+
+ assert(var != NULL);
+
+ if (var->max_array_access >= unsigned(rhs->type->array_size())) {
+ /* FINISHME: This should actually log the location of the RHS. */
+ _mesa_glsl_error(& lhs_loc, state, "array size must be > %u due to "
+ "previous access",
+ var->max_array_access);
+ }
+
+ var->type = glsl_type::get_array_instance(lhs->type->element_type(),
+ rhs->type->array_size());
+ d->type = var->type;
+ }
+ }
+
+ /* Most callers of do_assignment (assign, add_assign, pre_inc/dec,
+ * but not post_inc) need the converted assigned value as an rvalue
+ * to handle things like:
+ *
+ * i = j += 1;
+ *
+ * So we always just store the computed value being assigned to a
+ * temporary and return a deref of that temporary. If the rvalue
+ * ends up not being used, the temp will get copy-propagated out.
+ */
+ ir_variable *var = new(ctx) ir_variable(rhs->type, "assignment_tmp",
+ ir_var_temporary);
+ ir_dereference_variable *deref_var = new(ctx) ir_dereference_variable(var);
+ instructions->push_tail(var);
+ instructions->push_tail(new(ctx) ir_assignment(deref_var,
+ rhs,
+ NULL));
+ deref_var = new(ctx) ir_dereference_variable(var);
+
+ if (!error_emitted)
+ instructions->push_tail(new(ctx) ir_assignment(lhs, deref_var, NULL));
+
+ return new(ctx) ir_dereference_variable(var);
+}
+
+static ir_rvalue *
+get_lvalue_copy(exec_list *instructions, ir_rvalue *lvalue)
+{
+ void *ctx = ralloc_parent(lvalue);
+ ir_variable *var;
+
+ var = new(ctx) ir_variable(lvalue->type, "_post_incdec_tmp",
+ ir_var_temporary);
+ instructions->push_tail(var);
+ var->mode = ir_var_auto;
+
+ instructions->push_tail(new(ctx) ir_assignment(new(ctx) ir_dereference_variable(var),
+ lvalue, NULL));
+
+ /* Once we've created this temporary, mark it read only so it's no
+ * longer considered an lvalue.
+ */
+ var->read_only = true;
+
+ return new(ctx) ir_dereference_variable(var);
+}
+
+
+ir_rvalue *
+ast_node::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ (void) instructions;
+ (void) state;
+
+ return NULL;
+}
+
+static void
+mark_whole_array_access(ir_rvalue *access)
+{
+ ir_dereference_variable *deref = access->as_dereference_variable();
+
+ if (deref) {
+ deref->var->max_array_access = deref->type->length - 1;
+ }
+}
+
+static ir_rvalue *
+do_comparison(void *mem_ctx, int operation, ir_rvalue *op0, ir_rvalue *op1)
+{
+ int join_op;
+ ir_rvalue *cmp = NULL;
+
+ if (operation == ir_binop_all_equal)
+ join_op = ir_binop_logic_and;
+ else
+ join_op = ir_binop_logic_or;
+
+ switch (op0->type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ case GLSL_TYPE_BOOL:
+ return new(mem_ctx) ir_expression(operation, op0, op1);
+
+ case GLSL_TYPE_ARRAY: {
+ for (unsigned int i = 0; i < op0->type->length; i++) {
+ ir_rvalue *e0, *e1, *result;
+
+ e0 = new(mem_ctx) ir_dereference_array(op0->clone(mem_ctx, NULL),
+ new(mem_ctx) ir_constant(i));
+ e1 = new(mem_ctx) ir_dereference_array(op1->clone(mem_ctx, NULL),
+ new(mem_ctx) ir_constant(i));
+ result = do_comparison(mem_ctx, operation, e0, e1);
+
+ if (cmp) {
+ cmp = new(mem_ctx) ir_expression(join_op, cmp, result);
+ } else {
+ cmp = result;
+ }
+ }
+
+ mark_whole_array_access(op0);
+ mark_whole_array_access(op1);
+ break;
+ }
+
+ case GLSL_TYPE_STRUCT: {
+ for (unsigned int i = 0; i < op0->type->length; i++) {
+ ir_rvalue *e0, *e1, *result;
+ const char *field_name = op0->type->fields.structure[i].name;
+
+ e0 = new(mem_ctx) ir_dereference_record(op0->clone(mem_ctx, NULL),
+ field_name);
+ e1 = new(mem_ctx) ir_dereference_record(op1->clone(mem_ctx, NULL),
+ field_name);
+ result = do_comparison(mem_ctx, operation, e0, e1);
+
+ if (cmp) {
+ cmp = new(mem_ctx) ir_expression(join_op, cmp, result);
+ } else {
+ cmp = result;
+ }
+ }
+ break;
+ }
+
+ case GLSL_TYPE_ERROR:
+ case GLSL_TYPE_VOID:
+ case GLSL_TYPE_SAMPLER:
+ /* I assume a comparison of a struct containing a sampler just
+ * ignores the sampler present in the type.
+ */
+ break;
+
+ default:
+ assert(!"Should not get here.");
+ break;
+ }
+
+ if (cmp == NULL)
+ cmp = new(mem_ctx) ir_constant(true);
+
+ return cmp;
+}
+
+ir_rvalue *
+ast_expression::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ static const int operations[AST_NUM_OPERATORS] = {
+ -1, /* ast_assign doesn't convert to ir_expression. */
+ -1, /* ast_plus doesn't convert to ir_expression. */
+ ir_unop_neg,
+ ir_binop_add,
+ ir_binop_sub,
+ ir_binop_mul,
+ ir_binop_div,
+ ir_binop_mod,
+ ir_binop_lshift,
+ ir_binop_rshift,
+ ir_binop_less,
+ ir_binop_greater,
+ ir_binop_lequal,
+ ir_binop_gequal,
+ ir_binop_all_equal,
+ ir_binop_any_nequal,
+ ir_binop_bit_and,
+ ir_binop_bit_xor,
+ ir_binop_bit_or,
+ ir_unop_bit_not,
+ ir_binop_logic_and,
+ ir_binop_logic_xor,
+ ir_binop_logic_or,
+ ir_unop_logic_not,
+
+ /* Note: The following block of expression types actually convert
+ * to multiple IR instructions.
+ */
+ ir_binop_mul, /* ast_mul_assign */
+ ir_binop_div, /* ast_div_assign */
+ ir_binop_mod, /* ast_mod_assign */
+ ir_binop_add, /* ast_add_assign */
+ ir_binop_sub, /* ast_sub_assign */
+ ir_binop_lshift, /* ast_ls_assign */
+ ir_binop_rshift, /* ast_rs_assign */
+ ir_binop_bit_and, /* ast_and_assign */
+ ir_binop_bit_xor, /* ast_xor_assign */
+ ir_binop_bit_or, /* ast_or_assign */
+
+ -1, /* ast_conditional doesn't convert to ir_expression. */
+ ir_binop_add, /* ast_pre_inc. */
+ ir_binop_sub, /* ast_pre_dec. */
+ ir_binop_add, /* ast_post_inc. */
+ ir_binop_sub, /* ast_post_dec. */
+ -1, /* ast_field_selection doesn't conv to ir_expression. */
+ -1, /* ast_array_index doesn't convert to ir_expression. */
+ -1, /* ast_function_call doesn't conv to ir_expression. */
+ -1, /* ast_identifier doesn't convert to ir_expression. */
+ -1, /* ast_int_constant doesn't convert to ir_expression. */
+ -1, /* ast_uint_constant doesn't conv to ir_expression. */
+ -1, /* ast_float_constant doesn't conv to ir_expression. */
+ -1, /* ast_bool_constant doesn't conv to ir_expression. */
+ -1, /* ast_sequence doesn't convert to ir_expression. */
+ };
+ ir_rvalue *result = NULL;
+ ir_rvalue *op[3];
+ const struct glsl_type *type = glsl_type::error_type;
+ bool error_emitted = false;
+ YYLTYPE loc;
+
+ loc = this->get_location();
+
+ switch (this->oper) {
+ case ast_assign: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ result = do_assignment(instructions, state, op[0], op[1], false,
+ this->subexpressions[0]->get_location());
+ error_emitted = result->type->is_error();
+ type = result->type;
+ break;
+ }
+
+ case ast_plus:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ type = unary_arithmetic_result_type(op[0]->type, state, & loc);
+
+ error_emitted = type->is_error();
+
+ result = op[0];
+ break;
+
+ case ast_neg:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ type = unary_arithmetic_result_type(op[0]->type, state, & loc);
+
+ error_emitted = type->is_error();
+
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], NULL);
+ break;
+
+ case ast_add:
+ case ast_sub:
+ case ast_mul:
+ case ast_div:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ type = arithmetic_result_type(op[0], op[1],
+ (this->oper == ast_mul),
+ state, & loc);
+ error_emitted = type->is_error();
+
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+ break;
+
+ case ast_mod:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ type = modulus_result_type(op[0]->type, op[1]->type, state, & loc);
+
+ assert(operations[this->oper] == ir_binop_mod);
+
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+ error_emitted = type->is_error();
+ break;
+
+ case ast_lshift:
+ case ast_rshift:
+ if (state->language_version < 130) {
+ _mesa_glsl_error(&loc, state, "operator %s requires GLSL 1.30",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+ type = shift_result_type(op[0]->type, op[1]->type, this->oper, state,
+ &loc);
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+ break;
+
+ case ast_less:
+ case ast_greater:
+ case ast_lequal:
+ case ast_gequal:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ type = relational_result_type(op[0], op[1], state, & loc);
+
+ /* The relational operators must either generate an error or result
+ * in a scalar boolean. See page 57 of the GLSL 1.50 spec.
+ */
+ assert(type->is_error()
+ || ((type->base_type == GLSL_TYPE_BOOL)
+ && type->is_scalar()));
+
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+ error_emitted = type->is_error();
+ break;
+
+ case ast_nequal:
+ case ast_equal:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ /* From page 58 (page 64 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "The equality operators equal (==), and not equal (!=)
+ * operate on all types. They result in a scalar Boolean. If
+ * the operand types do not match, then there must be a
+ * conversion from Section 4.1.10 "Implicit Conversions"
+ * applied to one operand that can make them match, in which
+ * case this conversion is done."
+ */
+ if ((!apply_implicit_conversion(op[0]->type, op[1], state)
+ && !apply_implicit_conversion(op[1]->type, op[0], state))
+ || (op[0]->type != op[1]->type)) {
+ _mesa_glsl_error(& loc, state, "operands of `%s' must have the same "
+ "type", (this->oper == ast_equal) ? "==" : "!=");
+ error_emitted = true;
+ } else if ((state->language_version <= 110)
+ && (op[0]->type->is_array() || op[1]->type->is_array())) {
+ _mesa_glsl_error(& loc, state, "array comparisons forbidden in "
+ "GLSL 1.10");
+ error_emitted = true;
+ }
+
+ result = do_comparison(ctx, operations[this->oper], op[0], op[1]);
+ type = glsl_type::bool_type;
+
+ assert(error_emitted || (result->type == glsl_type::bool_type));
+ break;
+
+ case ast_bit_and:
+ case ast_bit_xor:
+ case ast_bit_or:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+ type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper,
+ state, &loc);
+ result = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+ break;
+
+ case ast_bit_not:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ if (state->language_version < 130) {
+ _mesa_glsl_error(&loc, state, "bit-wise operations require GLSL 1.30");
+ error_emitted = true;
+ }
+
+ if (!op[0]->type->is_integer()) {
+ _mesa_glsl_error(&loc, state, "operand of `~' must be an integer");
+ error_emitted = true;
+ }
+
+ type = op[0]->type;
+ result = new(ctx) ir_expression(ir_unop_bit_not, type, op[0], NULL);
+ break;
+
+ case ast_logic_and: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[0]->get_location();
+
+ _mesa_glsl_error(& loc, state, "LHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_constant *op0_const = op[0]->constant_expression_value();
+ if (op0_const) {
+ if (op0_const->value.b[0]) {
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+ result = op[1];
+ } else {
+ result = op0_const;
+ }
+ type = glsl_type::bool_type;
+ } else {
+ ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
+ "and_tmp",
+ ir_var_temporary);
+ instructions->push_tail(tmp);
+
+ ir_if *const stmt = new(ctx) ir_if(op[0]);
+ instructions->push_tail(stmt);
+
+ op[1] = this->subexpressions[1]->hir(&stmt->then_instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const then_assign =
+ new(ctx) ir_assignment(then_deref, op[1], NULL);
+ stmt->then_instructions.push_tail(then_assign);
+
+ ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const else_assign =
+ new(ctx) ir_assignment(else_deref, new(ctx) ir_constant(false), NULL);
+ stmt->else_instructions.push_tail(else_assign);
+
+ result = new(ctx) ir_dereference_variable(tmp);
+ type = tmp->type;
+ }
+ break;
+ }
+
+ case ast_logic_or: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[0]->get_location();
+
+ _mesa_glsl_error(& loc, state, "LHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_constant *op0_const = op[0]->constant_expression_value();
+ if (op0_const) {
+ if (op0_const->value.b[0]) {
+ result = op0_const;
+ } else {
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+ result = op[1];
+ }
+ type = glsl_type::bool_type;
+ } else {
+ ir_variable *const tmp = new(ctx) ir_variable(glsl_type::bool_type,
+ "or_tmp",
+ ir_var_temporary);
+ instructions->push_tail(tmp);
+
+ ir_if *const stmt = new(ctx) ir_if(op[0]);
+ instructions->push_tail(stmt);
+
+ op[1] = this->subexpressions[1]->hir(&stmt->else_instructions, state);
+
+ if (!op[1]->type->is_boolean() || !op[1]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state, "RHS of `%s' must be scalar boolean",
+ operator_string(this->oper));
+ error_emitted = true;
+ }
+
+ ir_dereference *const then_deref = new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const then_assign =
+ new(ctx) ir_assignment(then_deref, new(ctx) ir_constant(true), NULL);
+ stmt->then_instructions.push_tail(then_assign);
+
+ ir_dereference *const else_deref = new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const else_assign =
+ new(ctx) ir_assignment(else_deref, op[1], NULL);
+ stmt->else_instructions.push_tail(else_assign);
+
+ result = new(ctx) ir_dereference_variable(tmp);
+ type = tmp->type;
+ }
+ break;
+ }
+
+ case ast_logic_xor:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+
+ result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type,
+ op[0], op[1]);
+ type = glsl_type::bool_type;
+ break;
+
+ case ast_logic_not:
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[0]->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "operand of `!' must be scalar boolean");
+ error_emitted = true;
+ }
+
+ result = new(ctx) ir_expression(operations[this->oper], glsl_type::bool_type,
+ op[0], NULL);
+ type = glsl_type::bool_type;
+ break;
+
+ case ast_mul_assign:
+ case ast_div_assign:
+ case ast_add_assign:
+ case ast_sub_assign: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ type = arithmetic_result_type(op[0], op[1],
+ (this->oper == ast_mul_assign),
+ state, & loc);
+
+ ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+
+ result = do_assignment(instructions, state,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
+ this->subexpressions[0]->get_location());
+ type = result->type;
+ error_emitted = (op[0]->type->is_error());
+
+ /* GLSL 1.10 does not allow array assignment. However, we don't have to
+ * explicitly test for this because none of the binary expression
+ * operators allow array operands either.
+ */
+
+ break;
+ }
+
+ case ast_mod_assign: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+
+ type = modulus_result_type(op[0]->type, op[1]->type, state, & loc);
+
+ assert(operations[this->oper] == ir_binop_mod);
+
+ ir_rvalue *temp_rhs;
+ temp_rhs = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+
+ result = do_assignment(instructions, state,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
+ this->subexpressions[0]->get_location());
+ type = result->type;
+ error_emitted = type->is_error();
+ break;
+ }
+
+ case ast_ls_assign:
+ case ast_rs_assign: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+ type = shift_result_type(op[0]->type, op[1]->type, this->oper, state,
+ &loc);
+ ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
+ type, op[0], op[1]);
+ result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
+ temp_rhs, false,
+ this->subexpressions[0]->get_location());
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+ break;
+ }
+
+ case ast_and_assign:
+ case ast_xor_assign:
+ case ast_or_assign: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ op[1] = this->subexpressions[1]->hir(instructions, state);
+ type = bit_logic_result_type(op[0]->type, op[1]->type, this->oper,
+ state, &loc);
+ ir_rvalue *temp_rhs = new(ctx) ir_expression(operations[this->oper],
+ type, op[0], op[1]);
+ result = do_assignment(instructions, state, op[0]->clone(ctx, NULL),
+ temp_rhs, false,
+ this->subexpressions[0]->get_location());
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+ break;
+ }
+
+ case ast_conditional: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+
+ /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "The ternary selection operator (?:). It operates on three
+ * expressions (exp1 ? exp2 : exp3). This operator evaluates the
+ * first expression, which must result in a scalar Boolean."
+ */
+ if (!op[0]->type->is_boolean() || !op[0]->type->is_scalar()) {
+ YYLTYPE loc = this->subexpressions[0]->get_location();
+
+ _mesa_glsl_error(& loc, state, "?: condition must be scalar boolean");
+ error_emitted = true;
+ }
+
+ /* The :? operator is implemented by generating an anonymous temporary
+ * followed by an if-statement. The last instruction in each branch of
+ * the if-statement assigns a value to the anonymous temporary. This
+ * temporary is the r-value of the expression.
+ */
+ exec_list then_instructions;
+ exec_list else_instructions;
+
+ op[1] = this->subexpressions[1]->hir(&then_instructions, state);
+ op[2] = this->subexpressions[2]->hir(&else_instructions, state);
+
+ /* From page 59 (page 65 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "The second and third expressions can be any type, as
+ * long their types match, or there is a conversion in
+ * Section 4.1.10 "Implicit Conversions" that can be applied
+ * to one of the expressions to make their types match. This
+ * resulting matching type is the type of the entire
+ * expression."
+ */
+ if ((!apply_implicit_conversion(op[1]->type, op[2], state)
+ && !apply_implicit_conversion(op[2]->type, op[1], state))
+ || (op[1]->type != op[2]->type)) {
+ YYLTYPE loc = this->subexpressions[1]->get_location();
+
+ _mesa_glsl_error(& loc, state, "Second and third operands of ?: "
+ "operator must have matching types.");
+ error_emitted = true;
+ type = glsl_type::error_type;
+ } else {
+ type = op[1]->type;
+ }
+
+ /* From page 33 (page 39 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "The second and third expressions must be the same type, but can
+ * be of any type other than an array."
+ */
+ if ((state->language_version <= 110) && type->is_array()) {
+ _mesa_glsl_error(& loc, state, "Second and third operands of ?: "
+ "operator must not be arrays.");
+ error_emitted = true;
+ }
+
+ ir_constant *cond_val = op[0]->constant_expression_value();
+ ir_constant *then_val = op[1]->constant_expression_value();
+ ir_constant *else_val = op[2]->constant_expression_value();
+
+ if (then_instructions.is_empty()
+ && else_instructions.is_empty()
+ && (cond_val != NULL) && (then_val != NULL) && (else_val != NULL)) {
+ result = (cond_val->value.b[0]) ? then_val : else_val;
+ } else {
+ ir_variable *const tmp =
+ new(ctx) ir_variable(type, "conditional_tmp", ir_var_temporary);
+ instructions->push_tail(tmp);
+
+ ir_if *const stmt = new(ctx) ir_if(op[0]);
+ instructions->push_tail(stmt);
+
+ then_instructions.move_nodes_to(& stmt->then_instructions);
+ ir_dereference *const then_deref =
+ new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const then_assign =
+ new(ctx) ir_assignment(then_deref, op[1], NULL);
+ stmt->then_instructions.push_tail(then_assign);
+
+ else_instructions.move_nodes_to(& stmt->else_instructions);
+ ir_dereference *const else_deref =
+ new(ctx) ir_dereference_variable(tmp);
+ ir_assignment *const else_assign =
+ new(ctx) ir_assignment(else_deref, op[2], NULL);
+ stmt->else_instructions.push_tail(else_assign);
+
+ result = new(ctx) ir_dereference_variable(tmp);
+ }
+ break;
+ }
+
+ case ast_pre_inc:
+ case ast_pre_dec: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ if (op[0]->type->base_type == GLSL_TYPE_FLOAT)
+ op[1] = new(ctx) ir_constant(1.0f);
+ else
+ op[1] = new(ctx) ir_constant(1);
+
+ type = arithmetic_result_type(op[0], op[1], false, state, & loc);
+
+ ir_rvalue *temp_rhs;
+ temp_rhs = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+
+ result = do_assignment(instructions, state,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
+ this->subexpressions[0]->get_location());
+ type = result->type;
+ error_emitted = op[0]->type->is_error();
+ break;
+ }
+
+ case ast_post_inc:
+ case ast_post_dec: {
+ op[0] = this->subexpressions[0]->hir(instructions, state);
+ if (op[0]->type->base_type == GLSL_TYPE_FLOAT)
+ op[1] = new(ctx) ir_constant(1.0f);
+ else
+ op[1] = new(ctx) ir_constant(1);
+
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+
+ type = arithmetic_result_type(op[0], op[1], false, state, & loc);
+
+ ir_rvalue *temp_rhs;
+ temp_rhs = new(ctx) ir_expression(operations[this->oper], type,
+ op[0], op[1]);
+
+ /* Get a temporary of a copy of the lvalue before it's modified.
+ * This may get thrown away later.
+ */
+ result = get_lvalue_copy(instructions, op[0]->clone(ctx, NULL));
+
+ (void)do_assignment(instructions, state,
+ op[0]->clone(ctx, NULL), temp_rhs, false,
+ this->subexpressions[0]->get_location());
+
+ type = result->type;
+ error_emitted = op[0]->type->is_error();
+ break;
+ }
+
+ case ast_field_selection:
+ result = _mesa_ast_field_selection_to_hir(this, instructions, state);
+ type = result->type;
+ break;
+
+ case ast_array_index: {
+ YYLTYPE index_loc = subexpressions[1]->get_location();
+
+ op[0] = subexpressions[0]->hir(instructions, state);
+ op[1] = subexpressions[1]->hir(instructions, state);
+
+ error_emitted = op[0]->type->is_error() || op[1]->type->is_error();
+
+ ir_rvalue *const array = op[0];
+
+ result = new(ctx) ir_dereference_array(op[0], op[1]);
+
+ /* Do not use op[0] after this point. Use array.
+ */
+ op[0] = NULL;
+
+
+ if (error_emitted)
+ break;
+
+ if (!array->type->is_array()
+ && !array->type->is_matrix()
+ && !array->type->is_vector()) {
+ _mesa_glsl_error(& index_loc, state,
+ "cannot dereference non-array / non-matrix / "
+ "non-vector");
+ error_emitted = true;
+ }
+
+ if (!op[1]->type->is_integer()) {
+ _mesa_glsl_error(& index_loc, state,
+ "array index must be integer type");
+ error_emitted = true;
+ } else if (!op[1]->type->is_scalar()) {
+ _mesa_glsl_error(& index_loc, state,
+ "array index must be scalar");
+ error_emitted = true;
+ }
+
+ /* If the array index is a constant expression and the array has a
+ * declared size, ensure that the access is in-bounds. If the array
+ * index is not a constant expression, ensure that the array has a
+ * declared size.
+ */
+ ir_constant *const const_index = op[1]->constant_expression_value();
+ if (const_index != NULL) {
+ const int idx = const_index->value.i[0];
+ const char *type_name;
+ unsigned bound = 0;
+
+ if (array->type->is_matrix()) {
+ type_name = "matrix";
+ } else if (array->type->is_vector()) {
+ type_name = "vector";
+ } else {
+ type_name = "array";
+ }
+
+ /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "It is illegal to declare an array with a size, and then
+ * later (in the same shader) index the same array with an
+ * integral constant expression greater than or equal to the
+ * declared size. It is also illegal to index an array with a
+ * negative constant expression."
+ */
+ if (array->type->is_matrix()) {
+ if (array->type->row_type()->vector_elements <= idx) {
+ bound = array->type->row_type()->vector_elements;
+ }
+ } else if (array->type->is_vector()) {
+ if (array->type->vector_elements <= idx) {
+ bound = array->type->vector_elements;
+ }
+ } else {
+ if ((array->type->array_size() > 0)
+ && (array->type->array_size() <= idx)) {
+ bound = array->type->array_size();
+ }
+ }
+
+ if (bound > 0) {
+ _mesa_glsl_error(& loc, state, "%s index must be < %u",
+ type_name, bound);
+ error_emitted = true;
+ } else if (idx < 0) {
+ _mesa_glsl_error(& loc, state, "%s index must be >= 0",
+ type_name);
+ error_emitted = true;
+ }
+
+ if (array->type->is_array()) {
+ /* If the array is a variable dereference, it dereferences the
+ * whole array, by definition. Use this to get the variable.
+ *
+ * FINISHME: Should some methods for getting / setting / testing
+ * FINISHME: array access limits be added to ir_dereference?
+ */
+ ir_variable *const v = array->whole_variable_referenced();
+ if ((v != NULL) && (unsigned(idx) > v->max_array_access))
+ v->max_array_access = idx;
+ }
+ } else if (array->type->array_size() == 0) {
+ _mesa_glsl_error(&loc, state, "unsized array index must be constant");
+ } else {
+ if (array->type->is_array()) {
+ /* whole_variable_referenced can return NULL if the array is a
+ * member of a structure. In this case it is safe to not update
+ * the max_array_access field because it is never used for fields
+ * of structures.
+ */
+ ir_variable *v = array->whole_variable_referenced();
+ if (v != NULL)
+ v->max_array_access = array->type->array_size();
+ }
+ }
+
+ /* From page 23 (29 of the PDF) of the GLSL 1.30 spec:
+ *
+ * "Samplers aggregated into arrays within a shader (using square
+ * brackets [ ]) can only be indexed with integral constant
+ * expressions [...]."
+ *
+ * This restriction was added in GLSL 1.30. Shaders using earlier version
+ * of the language should not be rejected by the compiler front-end for
+ * using this construct. This allows useful things such as using a loop
+ * counter as the index to an array of samplers. If the loop in unrolled,
+ * the code should compile correctly. Instead, emit a warning.
+ */
+ if (array->type->is_array() &&
+ array->type->element_type()->is_sampler() &&
+ const_index == NULL) {
+
+ if (state->language_version == 100) {
+ _mesa_glsl_warning(&loc, state,
+ "sampler arrays indexed with non-constant "
+ "expressions is optional in GLSL ES 1.00");
+ } else if (state->language_version < 130) {
+ _mesa_glsl_warning(&loc, state,
+ "sampler arrays indexed with non-constant "
+ "expressions is forbidden in GLSL 1.30 and "
+ "later");
+ } else {
+ _mesa_glsl_error(&loc, state,
+ "sampler arrays indexed with non-constant "
+ "expressions is forbidden in GLSL 1.30 and "
+ "later");
+ error_emitted = true;
+ }
+ }
+
+ if (error_emitted)
+ result->type = glsl_type::error_type;
+
+ type = result->type;
+ break;
+ }
+
+ case ast_function_call:
+ /* Should *NEVER* get here. ast_function_call should always be handled
+ * by ast_function_expression::hir.
+ */
+ assert(0);
+ break;
+
+ case ast_identifier: {
+ /* ast_identifier can appear several places in a full abstract syntax
+ * tree. This particular use must be at location specified in the grammar
+ * as 'variable_identifier'.
+ */
+ ir_variable *var =
+ state->symbols->get_variable(this->primary_expression.identifier);
+
+ result = new(ctx) ir_dereference_variable(var);
+
+ if (var != NULL) {
+ var->used = true;
+ type = result->type;
+ } else {
+ _mesa_glsl_error(& loc, state, "`%s' undeclared",
+ this->primary_expression.identifier);
+
+ error_emitted = true;
+ }
+ break;
+ }
+
+ case ast_int_constant:
+ type = glsl_type::int_type;
+ result = new(ctx) ir_constant(this->primary_expression.int_constant);
+ break;
+
+ case ast_uint_constant:
+ type = glsl_type::uint_type;
+ result = new(ctx) ir_constant(this->primary_expression.uint_constant);
+ break;
+
+ case ast_float_constant:
+ type = glsl_type::float_type;
+ result = new(ctx) ir_constant(this->primary_expression.float_constant);
+ break;
+
+ case ast_bool_constant:
+ type = glsl_type::bool_type;
+ result = new(ctx) ir_constant(bool(this->primary_expression.bool_constant));
+ break;
+
+ case ast_sequence: {
+ /* It should not be possible to generate a sequence in the AST without
+ * any expressions in it.
+ */
+ assert(!this->expressions.is_empty());
+
+ /* The r-value of a sequence is the last expression in the sequence. If
+ * the other expressions in the sequence do not have side-effects (and
+ * therefore add instructions to the instruction list), they get dropped
+ * on the floor.
+ */
+ foreach_list_typed (ast_node, ast, link, &this->expressions)
+ result = ast->hir(instructions, state);
+
+ type = result->type;
+
+ /* Any errors should have already been emitted in the loop above.
+ */
+ error_emitted = true;
+ break;
+ }
+ }
+
+ if (type->is_error() && !error_emitted)
+ _mesa_glsl_error(& loc, state, "type mismatch");
+
+ return result;
+}
+
+
+ir_rvalue *
+ast_expression_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ /* It is possible to have expression statements that don't have an
+ * expression. This is the solitary semicolon:
+ *
+ * for (i = 0; i < 5; i++)
+ * ;
+ *
+ * In this case the expression will be NULL. Test for NULL and don't do
+ * anything in that case.
+ */
+ if (expression != NULL)
+ expression->hir(instructions, state);
+
+ /* Statements do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_compound_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ if (new_scope)
+ state->symbols->push_scope();
+
+ foreach_list_typed (ast_node, ast, link, &this->statements)
+ ast->hir(instructions, state);
+
+ if (new_scope)
+ state->symbols->pop_scope();
+
+ /* Compound statements do not have r-values.
+ */
+ return NULL;
+}
+
+
+static const glsl_type *
+process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size,
+ struct _mesa_glsl_parse_state *state)
+{
+ unsigned length = 0;
+
+ /* FINISHME: Reject delcarations of multidimensional arrays. */
+
+ if (array_size != NULL) {
+ exec_list dummy_instructions;
+ ir_rvalue *const ir = array_size->hir(& dummy_instructions, state);
+ YYLTYPE loc = array_size->get_location();
+
+ /* FINISHME: Verify that the grammar forbids side-effects in array
+ * FINISHME: sizes. i.e., 'vec4 [x = 12] data'
+ */
+ assert(dummy_instructions.is_empty());
+
+ if (ir != NULL) {
+ if (!ir->type->is_integer()) {
+ _mesa_glsl_error(& loc, state, "array size must be integer type");
+ } else if (!ir->type->is_scalar()) {
+ _mesa_glsl_error(& loc, state, "array size must be scalar type");
+ } else {
+ ir_constant *const size = ir->constant_expression_value();
+
+ if (size == NULL) {
+ _mesa_glsl_error(& loc, state, "array size must be a "
+ "constant valued expression");
+ } else if (size->value.i[0] <= 0) {
+ _mesa_glsl_error(& loc, state, "array size must be > 0");
+ } else {
+ assert(size->type == ir->type);
+ length = size->value.u[0];
+ }
+ }
+ }
+ } else if (state->es_shader) {
+ /* Section 10.17 of the GLSL ES 1.00 specification states that unsized
+ * array declarations have been removed from the language.
+ */
+ _mesa_glsl_error(loc, state, "unsized array declarations are not "
+ "allowed in GLSL ES 1.00.");
+ }
+
+ return glsl_type::get_array_instance(base, length);
+}
+
+
+const glsl_type *
+ast_type_specifier::glsl_type(const char **name,
+ struct _mesa_glsl_parse_state *state) const
+{
+ const struct glsl_type *type;
+
+ type = state->symbols->get_type(this->type_name);
+ *name = this->type_name;
+
+ if (this->is_array) {
+ YYLTYPE loc = this->get_location();
+ type = process_array_type(&loc, type, this->array_size, state);
+ }
+
+ return type;
+}
+
+
+static void
+apply_type_qualifier_to_variable(const struct ast_type_qualifier *qual,
+ ir_variable *var,
+ struct _mesa_glsl_parse_state *state,
+ YYLTYPE *loc)
+{
+ if (qual->flags.q.invariant) {
+ if (var->used) {
+ _mesa_glsl_error(loc, state,
+ "variable `%s' may not be redeclared "
+ "`invariant' after being used",
+ var->name);
+ } else {
+ var->invariant = 1;
+ }
+ }
+
+ if (qual->flags.q.constant || qual->flags.q.attribute
+ || qual->flags.q.uniform
+ || (qual->flags.q.varying && (state->target == fragment_shader)))
+ var->read_only = 1;
+
+ if (qual->flags.q.centroid)
+ var->centroid = 1;
+
+ if (qual->flags.q.attribute && state->target != vertex_shader) {
+ var->type = glsl_type::error_type;
+ _mesa_glsl_error(loc, state,
+ "`attribute' variables may not be declared in the "
+ "%s shader",
+ _mesa_glsl_shader_target_name(state->target));
+ }
+
+ /* From page 25 (page 31 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "The varying qualifier can be used only with the data types
+ * float, vec2, vec3, vec4, mat2, mat3, and mat4, or arrays of
+ * these."
+ */
+ if (qual->flags.q.varying) {
+ const glsl_type *non_array_type;
+
+ if (var->type && var->type->is_array())
+ non_array_type = var->type->fields.array;
+ else
+ non_array_type = var->type;
+
+ if (non_array_type && non_array_type->base_type != GLSL_TYPE_FLOAT) {
+ var->type = glsl_type::error_type;
+ _mesa_glsl_error(loc, state,
+ "varying variables must be of base type float");
+ }
+ }
+
+ /* If there is no qualifier that changes the mode of the variable, leave
+ * the setting alone.
+ */
+ if (qual->flags.q.in && qual->flags.q.out)
+ var->mode = ir_var_inout;
+ else if (qual->flags.q.attribute || qual->flags.q.in
+ || (qual->flags.q.varying && (state->target == fragment_shader)))
+ var->mode = ir_var_in;
+ else if (qual->flags.q.out
+ || (qual->flags.q.varying && (state->target == vertex_shader)))
+ var->mode = ir_var_out;
+ else if (qual->flags.q.uniform)
+ var->mode = ir_var_uniform;
+
+ if (state->all_invariant && (state->current_function == NULL)) {
+ switch (state->target) {
+ case vertex_shader:
+ if (var->mode == ir_var_out)
+ var->invariant = true;
+ break;
+ case geometry_shader:
+ if ((var->mode == ir_var_in) || (var->mode == ir_var_out))
+ var->invariant = true;
+ break;
+ case fragment_shader:
+ if (var->mode == ir_var_in)
+ var->invariant = true;
+ break;
+ }
+ }
+
+ if (qual->flags.q.flat)
+ var->interpolation = ir_var_flat;
+ else if (qual->flags.q.noperspective)
+ var->interpolation = ir_var_noperspective;
+ else
+ var->interpolation = ir_var_smooth;
+
+ var->pixel_center_integer = qual->flags.q.pixel_center_integer;
+ var->origin_upper_left = qual->flags.q.origin_upper_left;
+ if ((qual->flags.q.origin_upper_left || qual->flags.q.pixel_center_integer)
+ && (strcmp(var->name, "gl_FragCoord") != 0)) {
+ const char *const qual_string = (qual->flags.q.origin_upper_left)
+ ? "origin_upper_left" : "pixel_center_integer";
+
+ _mesa_glsl_error(loc, state,
+ "layout qualifier `%s' can only be applied to "
+ "fragment shader input `gl_FragCoord'",
+ qual_string);
+ }
+
+ if (qual->flags.q.explicit_location) {
+ const bool global_scope = (state->current_function == NULL);
+ bool fail = false;
+ const char *string = "";
+
+ /* In the vertex shader only shader inputs can be given explicit
+ * locations.
+ *
+ * In the fragment shader only shader outputs can be given explicit
+ * locations.
+ */
+ switch (state->target) {
+ case vertex_shader:
+ if (!global_scope || (var->mode != ir_var_in)) {
+ fail = true;
+ string = "input";
+ }
+ break;
+
+ case geometry_shader:
+ _mesa_glsl_error(loc, state,
+ "geometry shader variables cannot be given "
+ "explicit locations\n");
+ break;
+
+ case fragment_shader:
+ if (!global_scope || (var->mode != ir_var_in)) {
+ fail = true;
+ string = "output";
+ }
+ break;
+ };
+
+ if (fail) {
+ _mesa_glsl_error(loc, state,
+ "only %s shader %s variables can be given an "
+ "explicit location\n",
+ _mesa_glsl_shader_target_name(state->target),
+ string);
+ } else {
+ var->explicit_location = true;
+
+ /* This bit of silliness is needed because invalid explicit locations
+ * are supposed to be flagged during linking. Small negative values
+ * biased by VERT_ATTRIB_GENERIC0 or FRAG_RESULT_DATA0 could alias
+ * built-in values (e.g., -16+VERT_ATTRIB_GENERIC0 = VERT_ATTRIB_POS).
+ * The linker needs to be able to differentiate these cases. This
+ * ensures that negative values stay negative.
+ */
+ if (qual->location >= 0) {
+ var->location = (state->target == vertex_shader)
+ ? (qual->location + VERT_ATTRIB_GENERIC0)
+ : (qual->location + FRAG_RESULT_DATA0);
+ } else {
+ var->location = qual->location;
+ }
+ }
+ }
+
+ /* Does the declaration use the 'layout' keyword?
+ */
+ const bool uses_layout = qual->flags.q.pixel_center_integer
+ || qual->flags.q.origin_upper_left
+ || qual->flags.q.explicit_location;
+
+ /* Does the declaration use the deprecated 'attribute' or 'varying'
+ * keywords?
+ */
+ const bool uses_deprecated_qualifier = qual->flags.q.attribute
+ || qual->flags.q.varying;
+
+ /* Is the 'layout' keyword used with parameters that allow relaxed checking.
+ * Many implementations of GL_ARB_fragment_coord_conventions_enable and some
+ * implementations (only Mesa?) GL_ARB_explicit_attrib_location_enable
+ * allowed the layout qualifier to be used with 'varying' and 'attribute'.
+ * These extensions and all following extensions that add the 'layout'
+ * keyword have been modified to require the use of 'in' or 'out'.
+ *
+ * The following extension do not allow the deprecated keywords:
+ *
+ * GL_AMD_conservative_depth
+ * GL_ARB_gpu_shader5
+ * GL_ARB_separate_shader_objects
+ * GL_ARB_tesselation_shader
+ * GL_ARB_transform_feedback3
+ * GL_ARB_uniform_buffer_object
+ *
+ * It is unknown whether GL_EXT_shader_image_load_store or GL_NV_gpu_shader5
+ * allow layout with the deprecated keywords.
+ */
+ const bool relaxed_layout_qualifier_checking =
+ state->ARB_fragment_coord_conventions_enable;
+
+ if (uses_layout && uses_deprecated_qualifier) {
+ if (relaxed_layout_qualifier_checking) {
+ _mesa_glsl_warning(loc, state,
+ "`layout' qualifier may not be used with "
+ "`attribute' or `varying'");
+ } else {
+ _mesa_glsl_error(loc, state,
+ "`layout' qualifier may not be used with "
+ "`attribute' or `varying'");
+ }
+ }
+
+ /* Layout qualifiers for gl_FragDepth, which are enabled by extension
+ * AMD_conservative_depth.
+ */
+ int depth_layout_count = qual->flags.q.depth_any
+ + qual->flags.q.depth_greater
+ + qual->flags.q.depth_less
+ + qual->flags.q.depth_unchanged;
+ if (depth_layout_count > 0
+ && !state->AMD_conservative_depth_enable) {
+ _mesa_glsl_error(loc, state,
+ "extension GL_AMD_conservative_depth must be enabled "
+ "to use depth layout qualifiers");
+ } else if (depth_layout_count > 0
+ && strcmp(var->name, "gl_FragDepth") != 0) {
+ _mesa_glsl_error(loc, state,
+ "depth layout qualifiers can be applied only to "
+ "gl_FragDepth");
+ } else if (depth_layout_count > 1
+ && strcmp(var->name, "gl_FragDepth") == 0) {
+ _mesa_glsl_error(loc, state,
+ "at most one depth layout qualifier can be applied to "
+ "gl_FragDepth");
+ }
+ if (qual->flags.q.depth_any)
+ var->depth_layout = ir_depth_layout_any;
+ else if (qual->flags.q.depth_greater)
+ var->depth_layout = ir_depth_layout_greater;
+ else if (qual->flags.q.depth_less)
+ var->depth_layout = ir_depth_layout_less;
+ else if (qual->flags.q.depth_unchanged)
+ var->depth_layout = ir_depth_layout_unchanged;
+ else
+ var->depth_layout = ir_depth_layout_none;
+
+ if (var->type->is_array() && state->language_version != 110) {
+ var->array_lvalue = true;
+ }
+}
+
+/**
+ * Get the variable that is being redeclared by this declaration
+ *
+ * Semantic checks to verify the validity of the redeclaration are also
+ * performed. If semantic checks fail, compilation error will be emitted via
+ * \c _mesa_glsl_error, but a non-\c NULL pointer will still be returned.
+ *
+ * \returns
+ * A pointer to an existing variable in the current scope if the declaration
+ * is a redeclaration, \c NULL otherwise.
+ */
+ir_variable *
+get_variable_being_redeclared(ir_variable *var, ast_declaration *decl,
+ struct _mesa_glsl_parse_state *state)
+{
+ /* Check if this declaration is actually a re-declaration, either to
+ * resize an array or add qualifiers to an existing variable.
+ *
+ * This is allowed for variables in the current scope, or when at
+ * global scope (for built-ins in the implicit outer scope).
+ */
+ ir_variable *earlier = state->symbols->get_variable(decl->identifier);
+ if (earlier == NULL ||
+ (state->current_function != NULL &&
+ !state->symbols->name_declared_this_scope(decl->identifier))) {
+ return NULL;
+ }
+
+
+ YYLTYPE loc = decl->get_location();
+
+ /* From page 24 (page 30 of the PDF) of the GLSL 1.50 spec,
+ *
+ * "It is legal to declare an array without a size and then
+ * later re-declare the same name as an array of the same
+ * type and specify a size."
+ */
+ if ((earlier->type->array_size() == 0)
+ && var->type->is_array()
+ && (var->type->element_type() == earlier->type->element_type())) {
+ /* FINISHME: This doesn't match the qualifiers on the two
+ * FINISHME: declarations. It's not 100% clear whether this is
+ * FINISHME: required or not.
+ */
+
+ /* From page 54 (page 60 of the PDF) of the GLSL 1.20 spec:
+ *
+ * "The size [of gl_TexCoord] can be at most
+ * gl_MaxTextureCoords."
+ */
+ const unsigned size = unsigned(var->type->array_size());
+ if ((strcmp("gl_TexCoord", var->name) == 0)
+ && (size > state->Const.MaxTextureCoords)) {
+ _mesa_glsl_error(& loc, state, "`gl_TexCoord' array size cannot "
+ "be larger than gl_MaxTextureCoords (%u)\n",
+ state->Const.MaxTextureCoords);
+ } else if ((size > 0) && (size <= earlier->max_array_access)) {
+ _mesa_glsl_error(& loc, state, "array size must be > %u due to "
+ "previous access",
+ earlier->max_array_access);
+ }
+
+ earlier->type = var->type;
+ delete var;
+ var = NULL;
+ } else if (state->ARB_fragment_coord_conventions_enable
+ && strcmp(var->name, "gl_FragCoord") == 0
+ && earlier->type == var->type
+ && earlier->mode == var->mode) {
+ /* Allow redeclaration of gl_FragCoord for ARB_fcc layout
+ * qualifiers.
+ */
+ earlier->origin_upper_left = var->origin_upper_left;
+ earlier->pixel_center_integer = var->pixel_center_integer;
+
+ /* According to section 4.3.7 of the GLSL 1.30 spec,
+ * the following built-in varaibles can be redeclared with an
+ * interpolation qualifier:
+ * * gl_FrontColor
+ * * gl_BackColor
+ * * gl_FrontSecondaryColor
+ * * gl_BackSecondaryColor
+ * * gl_Color
+ * * gl_SecondaryColor
+ */
+ } else if (state->language_version >= 130
+ && (strcmp(var->name, "gl_FrontColor") == 0
+ || strcmp(var->name, "gl_BackColor") == 0
+ || strcmp(var->name, "gl_FrontSecondaryColor") == 0
+ || strcmp(var->name, "gl_BackSecondaryColor") == 0
+ || strcmp(var->name, "gl_Color") == 0
+ || strcmp(var->name, "gl_SecondaryColor") == 0)
+ && earlier->type == var->type
+ && earlier->mode == var->mode) {
+ earlier->interpolation = var->interpolation;
+
+ /* Layout qualifiers for gl_FragDepth. */
+ } else if (state->AMD_conservative_depth_enable
+ && strcmp(var->name, "gl_FragDepth") == 0
+ && earlier->type == var->type
+ && earlier->mode == var->mode) {
+
+ /** From the AMD_conservative_depth spec:
+ * Within any shader, the first redeclarations of gl_FragDepth
+ * must appear before any use of gl_FragDepth.
+ */
+ if (earlier->used) {
+ _mesa_glsl_error(&loc, state,
+ "the first redeclaration of gl_FragDepth "
+ "must appear before any use of gl_FragDepth");
+ }
+
+ /* Prevent inconsistent redeclaration of depth layout qualifier. */
+ if (earlier->depth_layout != ir_depth_layout_none
+ && earlier->depth_layout != var->depth_layout) {
+ _mesa_glsl_error(&loc, state,
+ "gl_FragDepth: depth layout is declared here "
+ "as '%s, but it was previously declared as "
+ "'%s'",
+ depth_layout_string(var->depth_layout),
+ depth_layout_string(earlier->depth_layout));
+ }
+
+ earlier->depth_layout = var->depth_layout;
+
+ } else {
+ _mesa_glsl_error(&loc, state, "`%s' redeclared", decl->identifier);
+ }
+
+ return earlier;
+}
+
+/**
+ * Generate the IR for an initializer in a variable declaration
+ */
+ir_rvalue *
+process_initializer(ir_variable *var, ast_declaration *decl,
+ ast_fully_specified_type *type,
+ exec_list *initializer_instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ ir_rvalue *result = NULL;
+
+ YYLTYPE initializer_loc = decl->initializer->get_location();
+
+ /* From page 24 (page 30 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "All uniform variables are read-only and are initialized either
+ * directly by an application via API commands, or indirectly by
+ * OpenGL."
+ */
+ if ((state->language_version <= 110)
+ && (var->mode == ir_var_uniform)) {
+ _mesa_glsl_error(& initializer_loc, state,
+ "cannot initialize uniforms in GLSL 1.10");
+ }
+
+ if (var->type->is_sampler()) {
+ _mesa_glsl_error(& initializer_loc, state,
+ "cannot initialize samplers");
+ }
+
+ if ((var->mode == ir_var_in) && (state->current_function == NULL)) {
+ _mesa_glsl_error(& initializer_loc, state,
+ "cannot initialize %s shader input / %s",
+ _mesa_glsl_shader_target_name(state->target),
+ (state->target == vertex_shader)
+ ? "attribute" : "varying");
+ }
+
+ ir_dereference *const lhs = new(state) ir_dereference_variable(var);
+ ir_rvalue *rhs = decl->initializer->hir(initializer_instructions,
+ state);
+
+ /* Calculate the constant value if this is a const or uniform
+ * declaration.
+ */
+ if (type->qualifier.flags.q.constant
+ || type->qualifier.flags.q.uniform) {
+ ir_rvalue *new_rhs = validate_assignment(state, var->type, rhs, true);
+ if (new_rhs != NULL) {
+ rhs = new_rhs;
+
+ ir_constant *constant_value = rhs->constant_expression_value();
+ if (!constant_value) {
+ _mesa_glsl_error(& initializer_loc, state,
+ "initializer of %s variable `%s' must be a "
+ "constant expression",
+ (type->qualifier.flags.q.constant)
+ ? "const" : "uniform",
+ decl->identifier);
+ if (var->type->is_numeric()) {
+ /* Reduce cascading errors. */
+ var->constant_value = ir_constant::zero(state, var->type);
+ }
+ } else {
+ rhs = constant_value;
+ var->constant_value = constant_value;
+ }
+ } else {
+ _mesa_glsl_error(&initializer_loc, state,
+ "initializer of type %s cannot be assigned to "
+ "variable of type %s",
+ rhs->type->name, var->type->name);
+ if (var->type->is_numeric()) {
+ /* Reduce cascading errors. */
+ var->constant_value = ir_constant::zero(state, var->type);
+ }
+ }
+ }
+
+ if (rhs && !rhs->type->is_error()) {
+ bool temp = var->read_only;
+ if (type->qualifier.flags.q.constant)
+ var->read_only = false;
+
+ /* Never emit code to initialize a uniform.
+ */
+ const glsl_type *initializer_type;
+ if (!type->qualifier.flags.q.uniform) {
+ result = do_assignment(initializer_instructions, state,
+ lhs, rhs, true,
+ type->get_location());
+ initializer_type = result->type;
+ } else
+ initializer_type = rhs->type;
+
+ /* If the declared variable is an unsized array, it must inherrit
+ * its full type from the initializer. A declaration such as
+ *
+ * uniform float a[] = float[](1.0, 2.0, 3.0, 3.0);
+ *
+ * becomes
+ *
+ * uniform float a[4] = float[](1.0, 2.0, 3.0, 3.0);
+ *
+ * The assignment generated in the if-statement (below) will also
+ * automatically handle this case for non-uniforms.
+ *
+ * If the declared variable is not an array, the types must
+ * already match exactly. As a result, the type assignment
+ * here can be done unconditionally. For non-uniforms the call
+ * to do_assignment can change the type of the initializer (via
+ * the implicit conversion rules). For uniforms the initializer
+ * must be a constant expression, and the type of that expression
+ * was validated above.
+ */
+ var->type = initializer_type;
+
+ var->read_only = temp;
+ }
+
+ return result;
+}
+
+ir_rvalue *
+ast_declarator_list::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ const struct glsl_type *decl_type;
+ const char *type_name = NULL;
+ ir_rvalue *result = NULL;
+ YYLTYPE loc = this->get_location();
+
+ /* From page 46 (page 52 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "To ensure that a particular output variable is invariant, it is
+ * necessary to use the invariant qualifier. It can either be used to
+ * qualify a previously declared variable as being invariant
+ *
+ * invariant gl_Position; // make existing gl_Position be invariant"
+ *
+ * In these cases the parser will set the 'invariant' flag in the declarator
+ * list, and the type will be NULL.
+ */
+ if (this->invariant) {
+ assert(this->type == NULL);
+
+ if (state->current_function != NULL) {
+ _mesa_glsl_error(& loc, state,
+ "All uses of `invariant' keyword must be at global "
+ "scope\n");
+ }
+
+ foreach_list_typed (ast_declaration, decl, link, &this->declarations) {
+ assert(!decl->is_array);
+ assert(decl->array_size == NULL);
+ assert(decl->initializer == NULL);
+
+ ir_variable *const earlier =
+ state->symbols->get_variable(decl->identifier);
+ if (earlier == NULL) {
+ _mesa_glsl_error(& loc, state,
+ "Undeclared variable `%s' cannot be marked "
+ "invariant\n", decl->identifier);
+ } else if ((state->target == vertex_shader)
+ && (earlier->mode != ir_var_out)) {
+ _mesa_glsl_error(& loc, state,
+ "`%s' cannot be marked invariant, vertex shader "
+ "outputs only\n", decl->identifier);
+ } else if ((state->target == fragment_shader)
+ && (earlier->mode != ir_var_in)) {
+ _mesa_glsl_error(& loc, state,
+ "`%s' cannot be marked invariant, fragment shader "
+ "inputs only\n", decl->identifier);
+ } else if (earlier->used) {
+ _mesa_glsl_error(& loc, state,
+ "variable `%s' may not be redeclared "
+ "`invariant' after being used",
+ earlier->name);
+ } else {
+ earlier->invariant = true;
+ }
+ }
+
+ /* Invariant redeclarations do not have r-values.
+ */
+ return NULL;
+ }
+
+ assert(this->type != NULL);
+ assert(!this->invariant);
+
+ /* The type specifier may contain a structure definition. Process that
+ * before any of the variable declarations.
+ */
+ (void) this->type->specifier->hir(instructions, state);
+
+ decl_type = this->type->specifier->glsl_type(& type_name, state);
+ if (this->declarations.is_empty()) {
+ /* The only valid case where the declaration list can be empty is when
+ * the declaration is setting the default precision of a built-in type
+ * (e.g., 'precision highp vec4;').
+ */
+
+ if (decl_type != NULL) {
+ } else {
+ _mesa_glsl_error(& loc, state, "incomplete declaration");
+ }
+ }
+
+ foreach_list_typed (ast_declaration, decl, link, &this->declarations) {
+ const struct glsl_type *var_type;
+ ir_variable *var;
+
+ /* FINISHME: Emit a warning if a variable declaration shadows a
+ * FINISHME: declaration at a higher scope.
+ */
+
+ if ((decl_type == NULL) || decl_type->is_void()) {
+ if (type_name != NULL) {
+ _mesa_glsl_error(& loc, state,
+ "invalid type `%s' in declaration of `%s'",
+ type_name, decl->identifier);
+ } else {
+ _mesa_glsl_error(& loc, state,
+ "invalid type in declaration of `%s'",
+ decl->identifier);
+ }
+ continue;
+ }
+
+ if (decl->is_array) {
+ var_type = process_array_type(&loc, decl_type, decl->array_size,
+ state);
+ } else {
+ var_type = decl_type;
+ }
+
+ var = new(ctx) ir_variable(var_type, decl->identifier, ir_var_auto);
+
+ /* From page 22 (page 28 of the PDF) of the GLSL 1.10 specification;
+ *
+ * "Global variables can only use the qualifiers const,
+ * attribute, uni form, or varying. Only one may be
+ * specified.
+ *
+ * Local variables can only use the qualifier const."
+ *
+ * This is relaxed in GLSL 1.30. It is also relaxed by any extension
+ * that adds the 'layout' keyword.
+ */
+ if ((state->language_version < 130)
+ && !state->ARB_explicit_attrib_location_enable
+ && !state->ARB_fragment_coord_conventions_enable) {
+ if (this->type->qualifier.flags.q.out) {
+ _mesa_glsl_error(& loc, state,
+ "`out' qualifier in declaration of `%s' "
+ "only valid for function parameters in %s.",
+ decl->identifier, state->version_string);
+ }
+ if (this->type->qualifier.flags.q.in) {
+ _mesa_glsl_error(& loc, state,
+ "`in' qualifier in declaration of `%s' "
+ "only valid for function parameters in %s.",
+ decl->identifier, state->version_string);
+ }
+ /* FINISHME: Test for other invalid qualifiers. */
+ }
+
+ apply_type_qualifier_to_variable(& this->type->qualifier, var, state,
+ & loc);
+
+ if (this->type->qualifier.flags.q.invariant) {
+ if ((state->target == vertex_shader) && !(var->mode == ir_var_out ||
+ var->mode == ir_var_inout)) {
+ /* FINISHME: Note that this doesn't work for invariant on
+ * a function signature outval
+ */
+ _mesa_glsl_error(& loc, state,
+ "`%s' cannot be marked invariant, vertex shader "
+ "outputs only\n", var->name);
+ } else if ((state->target == fragment_shader) &&
+ !(var->mode == ir_var_in || var->mode == ir_var_inout)) {
+ /* FINISHME: Note that this doesn't work for invariant on
+ * a function signature inval
+ */
+ _mesa_glsl_error(& loc, state,
+ "`%s' cannot be marked invariant, fragment shader "
+ "inputs only\n", var->name);
+ }
+ }
+
+ if (state->current_function != NULL) {
+ const char *mode = NULL;
+ const char *extra = "";
+
+ /* There is no need to check for 'inout' here because the parser will
+ * only allow that in function parameter lists.
+ */
+ if (this->type->qualifier.flags.q.attribute) {
+ mode = "attribute";
+ } else if (this->type->qualifier.flags.q.uniform) {
+ mode = "uniform";
+ } else if (this->type->qualifier.flags.q.varying) {
+ mode = "varying";
+ } else if (this->type->qualifier.flags.q.in) {
+ mode = "in";
+ extra = " or in function parameter list";
+ } else if (this->type->qualifier.flags.q.out) {
+ mode = "out";
+ extra = " or in function parameter list";
+ }
+
+ if (mode) {
+ _mesa_glsl_error(& loc, state,
+ "%s variable `%s' must be declared at "
+ "global scope%s",
+ mode, var->name, extra);
+ }
+ } else if (var->mode == ir_var_in) {
+ var->read_only = true;
+
+ if (state->target == vertex_shader) {
+ bool error_emitted = false;
+
+ /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "Vertex shader inputs can only be float, floating-point
+ * vectors, matrices, signed and unsigned integers and integer
+ * vectors. Vertex shader inputs can also form arrays of these
+ * types, but not structures."
+ *
+ * From page 31 (page 27 of the PDF) of the GLSL 1.30 spec:
+ *
+ * "Vertex shader inputs can only be float, floating-point
+ * vectors, matrices, signed and unsigned integers and integer
+ * vectors. They cannot be arrays or structures."
+ *
+ * From page 23 (page 29 of the PDF) of the GLSL 1.20 spec:
+ *
+ * "The attribute qualifier can be used only with float,
+ * floating-point vectors, and matrices. Attribute variables
+ * cannot be declared as arrays or structures."
+ */
+ const glsl_type *check_type = var->type->is_array()
+ ? var->type->fields.array : var->type;
+
+ switch (check_type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ break;
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ if (state->language_version > 120)
+ break;
+ /* FALLTHROUGH */
+ default:
+ _mesa_glsl_error(& loc, state,
+ "vertex shader input / attribute cannot have "
+ "type %s`%s'",
+ var->type->is_array() ? "array of " : "",
+ check_type->name);
+ error_emitted = true;
+ }
+
+ if (!error_emitted && (state->language_version <= 130)
+ && var->type->is_array()) {
+ _mesa_glsl_error(& loc, state,
+ "vertex shader input / attribute cannot have "
+ "array type");
+ error_emitted = true;
+ }
+ }
+ }
+
+ /* Integer vertex outputs must be qualified with 'flat'.
+ *
+ * From section 4.3.6 of the GLSL 1.30 spec:
+ * "If a vertex output is a signed or unsigned integer or integer
+ * vector, then it must be qualified with the interpolation qualifier
+ * flat."
+ */
+ if (state->language_version >= 130
+ && state->target == vertex_shader
+ && state->current_function == NULL
+ && var->type->is_integer()
+ && var->mode == ir_var_out
+ && var->interpolation != ir_var_flat) {
+
+ _mesa_glsl_error(&loc, state, "If a vertex output is an integer, "
+ "then it must be qualified with 'flat'");
+ }
+
+
+ /* Interpolation qualifiers cannot be applied to 'centroid' and
+ * 'centroid varying'.
+ *
+ * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec:
+ * "interpolation qualifiers may only precede the qualifiers in,
+ * centroid in, out, or centroid out in a declaration. They do not apply
+ * to the deprecated storage qualifiers varying or centroid varying."
+ */
+ if (state->language_version >= 130
+ && this->type->qualifier.has_interpolation()
+ && this->type->qualifier.flags.q.varying) {
+
+ const char *i = this->type->qualifier.interpolation_string();
+ assert(i != NULL);
+ const char *s;
+ if (this->type->qualifier.flags.q.centroid)
+ s = "centroid varying";
+ else
+ s = "varying";
+
+ _mesa_glsl_error(&loc, state,
+ "qualifier '%s' cannot be applied to the "
+ "deprecated storage qualifier '%s'", i, s);
+ }
+
+
+ /* Interpolation qualifiers can only apply to vertex shader outputs and
+ * fragment shader inputs.
+ *
+ * From page 29 (page 35 of the PDF) of the GLSL 1.30 spec:
+ * "Outputs from a vertex shader (out) and inputs to a fragment
+ * shader (in) can be further qualified with one or more of these
+ * interpolation qualifiers"
+ */
+ if (state->language_version >= 130
+ && this->type->qualifier.has_interpolation()) {
+
+ const char *i = this->type->qualifier.interpolation_string();
+ assert(i != NULL);
+
+ switch (state->target) {
+ case vertex_shader:
+ if (this->type->qualifier.flags.q.in) {
+ _mesa_glsl_error(&loc, state,
+ "qualifier '%s' cannot be applied to vertex "
+ "shader inputs", i);
+ }
+ break;
+ case fragment_shader:
+ if (this->type->qualifier.flags.q.out) {
+ _mesa_glsl_error(&loc, state,
+ "qualifier '%s' cannot be applied to fragment "
+ "shader outputs", i);
+ }
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+
+ /* From section 4.3.4 of the GLSL 1.30 spec:
+ * "It is an error to use centroid in in a vertex shader."
+ */
+ if (state->language_version >= 130
+ && this->type->qualifier.flags.q.centroid
+ && this->type->qualifier.flags.q.in
+ && state->target == vertex_shader) {
+
+ _mesa_glsl_error(&loc, state,
+ "'centroid in' cannot be used in a vertex shader");
+ }
+
+
+ /* Precision qualifiers exists only in GLSL versions 1.00 and >= 1.30.
+ */
+ if (this->type->specifier->precision != ast_precision_none
+ && state->language_version != 100
+ && state->language_version < 130) {
+
+ _mesa_glsl_error(&loc, state,
+ "precision qualifiers are supported only in GLSL ES "
+ "1.00, and GLSL 1.30 and later");
+ }
+
+
+ /* Precision qualifiers only apply to floating point and integer types.
+ *
+ * From section 4.5.2 of the GLSL 1.30 spec:
+ * "Any floating point or any integer declaration can have the type
+ * preceded by one of these precision qualifiers [...] Literal
+ * constants do not have precision qualifiers. Neither do Boolean
+ * variables.
+ */
+ if (this->type->specifier->precision != ast_precision_none
+ && !var->type->is_float()
+ && !var->type->is_integer()
+ && !(var->type->is_array()
+ && (var->type->fields.array->is_float()
+ || var->type->fields.array->is_integer()))) {
+
+ _mesa_glsl_error(&loc, state,
+ "precision qualifiers apply only to floating point "
+ "and integer types");
+ }
+
+ /* Process the initializer and add its instructions to a temporary
+ * list. This list will be added to the instruction stream (below) after
+ * the declaration is added. This is done because in some cases (such as
+ * redeclarations) the declaration may not actually be added to the
+ * instruction stream.
+ */
+ exec_list initializer_instructions;
+ ir_variable *earlier = get_variable_being_redeclared(var, decl, state);
+
+ if (decl->initializer != NULL) {
+ result = process_initializer((earlier == NULL) ? var : earlier,
+ decl, this->type,
+ &initializer_instructions, state);
+ }
+
+ /* From page 23 (page 29 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "It is an error to write to a const variable outside of
+ * its declaration, so they must be initialized when
+ * declared."
+ */
+ if (this->type->qualifier.flags.q.constant && decl->initializer == NULL) {
+ _mesa_glsl_error(& loc, state,
+ "const declaration of `%s' must be initialized",
+ decl->identifier);
+ }
+
+ /* If the declaration is not a redeclaration, there are a few additional
+ * semantic checks that must be applied. In addition, variable that was
+ * created for the declaration should be added to the IR stream.
+ */
+ if (earlier == NULL) {
+ /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec,
+ *
+ * "Identifiers starting with "gl_" are reserved for use by
+ * OpenGL, and may not be declared in a shader as either a
+ * variable or a function."
+ */
+ if (strncmp(decl->identifier, "gl_", 3) == 0)
+ _mesa_glsl_error(& loc, state,
+ "identifier `%s' uses reserved `gl_' prefix",
+ decl->identifier);
+
+ /* Add the variable to the symbol table. Note that the initializer's
+ * IR was already processed earlier (though it hasn't been emitted
+ * yet), without the variable in scope.
+ *
+ * This differs from most C-like languages, but it follows the GLSL
+ * specification. From page 28 (page 34 of the PDF) of the GLSL 1.50
+ * spec:
+ *
+ * "Within a declaration, the scope of a name starts immediately
+ * after the initializer if present or immediately after the name
+ * being declared if not."
+ */
+ if (!state->symbols->add_variable(var)) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state, "name `%s' already taken in the "
+ "current scope", decl->identifier);
+ continue;
+ }
+
+ /* Push the variable declaration to the top. It means that all the
+ * variable declarations will appear in a funny last-to-first order,
+ * but otherwise we run into trouble if a function is prototyped, a
+ * global var is decled, then the function is defined with usage of
+ * the global var. See glslparsertest's CorrectModule.frag.
+ */
+ instructions->push_head(var);
+ }
+
+ instructions->append_list(&initializer_instructions);
+ }
+
+
+ /* Generally, variable declarations do not have r-values. However,
+ * one is used for the declaration in
+ *
+ * while (bool b = some_condition()) {
+ * ...
+ * }
+ *
+ * so we return the rvalue from the last seen declaration here.
+ */
+ return result;
+}
+
+
+ir_rvalue *
+ast_parameter_declarator::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ const struct glsl_type *type;
+ const char *name = NULL;
+ YYLTYPE loc = this->get_location();
+
+ type = this->type->specifier->glsl_type(& name, state);
+
+ if (type == NULL) {
+ if (name != NULL) {
+ _mesa_glsl_error(& loc, state,
+ "invalid type `%s' in declaration of `%s'",
+ name, this->identifier);
+ } else {
+ _mesa_glsl_error(& loc, state,
+ "invalid type in declaration of `%s'",
+ this->identifier);
+ }
+
+ type = glsl_type::error_type;
+ }
+
+ /* From page 62 (page 68 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "Functions that accept no input arguments need not use void in the
+ * argument list because prototypes (or definitions) are required and
+ * therefore there is no ambiguity when an empty argument list "( )" is
+ * declared. The idiom "(void)" as a parameter list is provided for
+ * convenience."
+ *
+ * Placing this check here prevents a void parameter being set up
+ * for a function, which avoids tripping up checks for main taking
+ * parameters and lookups of an unnamed symbol.
+ */
+ if (type->is_void()) {
+ if (this->identifier != NULL)
+ _mesa_glsl_error(& loc, state,
+ "named parameter cannot have type `void'");
+
+ is_void = true;
+ return NULL;
+ }
+
+ if (formal_parameter && (this->identifier == NULL)) {
+ _mesa_glsl_error(& loc, state, "formal parameter lacks a name");
+ return NULL;
+ }
+
+ /* This only handles "vec4 foo[..]". The earlier specifier->glsl_type(...)
+ * call already handled the "vec4[..] foo" case.
+ */
+ if (this->is_array) {
+ type = process_array_type(&loc, type, this->array_size, state);
+ }
+
+ if (type->array_size() == 0) {
+ _mesa_glsl_error(&loc, state, "arrays passed as parameters must have "
+ "a declared size.");
+ type = glsl_type::error_type;
+ }
+
+ is_void = false;
+ ir_variable *var = new(ctx) ir_variable(type, this->identifier, ir_var_in);
+
+ /* Apply any specified qualifiers to the parameter declaration. Note that
+ * for function parameters the default mode is 'in'.
+ */
+ apply_type_qualifier_to_variable(& this->type->qualifier, var, state, & loc);
+
+ instructions->push_tail(var);
+
+ /* Parameter declarations do not have r-values.
+ */
+ return NULL;
+}
+
+
+void
+ast_parameter_declarator::parameters_to_hir(exec_list *ast_parameters,
+ bool formal,
+ exec_list *ir_parameters,
+ _mesa_glsl_parse_state *state)
+{
+ ast_parameter_declarator *void_param = NULL;
+ unsigned count = 0;
+
+ foreach_list_typed (ast_parameter_declarator, param, link, ast_parameters) {
+ param->formal_parameter = formal;
+ param->hir(ir_parameters, state);
+
+ if (param->is_void)
+ void_param = param;
+
+ count++;
+ }
+
+ if ((void_param != NULL) && (count > 1)) {
+ YYLTYPE loc = void_param->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "`void' parameter must be only parameter");
+ }
+}
+
+
+void
+emit_function(_mesa_glsl_parse_state *state, exec_list *instructions,
+ ir_function *f)
+{
+ /* Emit the new function header */
+ if (state->current_function == NULL) {
+ instructions->push_tail(f);
+ } else {
+ /* IR invariants disallow function declarations or definitions nested
+ * within other function definitions. Insert the new ir_function
+ * block in the instruction sequence before the ir_function block
+ * containing the current ir_function_signature.
+ */
+ ir_function *const curr =
+ const_cast<ir_function *>(state->current_function->function());
+
+ curr->insert_before(f);
+ }
+}
+
+
+ir_rvalue *
+ast_function::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ ir_function *f = NULL;
+ ir_function_signature *sig = NULL;
+ exec_list hir_parameters;
+
+ const char *const name = identifier;
+
+ /* From page 21 (page 27 of the PDF) of the GLSL 1.20 spec,
+ *
+ * "Function declarations (prototypes) cannot occur inside of functions;
+ * they must be at global scope, or for the built-in functions, outside
+ * the global scope."
+ *
+ * From page 27 (page 33 of the PDF) of the GLSL ES 1.00.16 spec,
+ *
+ * "User defined functions may only be defined within the global scope."
+ *
+ * Note that this language does not appear in GLSL 1.10.
+ */
+ if ((state->current_function != NULL) && (state->language_version != 110)) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state,
+ "declaration of function `%s' not allowed within "
+ "function body", name);
+ }
+
+ /* From page 15 (page 21 of the PDF) of the GLSL 1.10 spec,
+ *
+ * "Identifiers starting with "gl_" are reserved for use by
+ * OpenGL, and may not be declared in a shader as either a
+ * variable or a function."
+ */
+ if (strncmp(name, "gl_", 3) == 0) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state,
+ "identifier `%s' uses reserved `gl_' prefix", name);
+ }
+
+ /* Convert the list of function parameters to HIR now so that they can be
+ * used below to compare this function's signature with previously seen
+ * signatures for functions with the same name.
+ */
+ ast_parameter_declarator::parameters_to_hir(& this->parameters,
+ is_definition,
+ & hir_parameters, state);
+
+ const char *return_type_name;
+ const glsl_type *return_type =
+ this->return_type->specifier->glsl_type(& return_type_name, state);
+
+ if (!return_type) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state,
+ "function `%s' has undeclared return type `%s'",
+ name, return_type_name);
+ return_type = glsl_type::error_type;
+ }
+
+ /* From page 56 (page 62 of the PDF) of the GLSL 1.30 spec:
+ * "No qualifier is allowed on the return type of a function."
+ */
+ if (this->return_type->has_qualifiers()) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(& loc, state,
+ "function `%s' return type has qualifiers", name);
+ }
+
+ /* Verify that this function's signature either doesn't match a previously
+ * seen signature for a function with the same name, or, if a match is found,
+ * that the previously seen signature does not have an associated definition.
+ */
+ f = state->symbols->get_function(name);
+ if (f != NULL && (state->es_shader || f->has_user_signature())) {
+ sig = f->exact_matching_signature(&hir_parameters);
+ if (sig != NULL) {
+ const char *badvar = sig->qualifiers_match(&hir_parameters);
+ if (badvar != NULL) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(&loc, state, "function `%s' parameter `%s' "
+ "qualifiers don't match prototype", name, badvar);
+ }
+
+ if (sig->return_type != return_type) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(&loc, state, "function `%s' return type doesn't "
+ "match prototype", name);
+ }
+
+ if (is_definition && sig->is_defined) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state, "function `%s' redefined", name);
+ }
+ }
+ } else {
+ f = new(ctx) ir_function(name);
+ if (!state->symbols->add_function(f)) {
+ /* This function name shadows a non-function use of the same name. */
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(&loc, state, "function name `%s' conflicts with "
+ "non-function", name);
+ return NULL;
+ }
+
+ emit_function(state, instructions, f);
+ }
+
+ /* Verify the return type of main() */
+ if (strcmp(name, "main") == 0) {
+ if (! return_type->is_void()) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state, "main() must return void");
+ }
+
+ if (!hir_parameters.is_empty()) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state, "main() must not take any parameters");
+ }
+ }
+
+ /* Finish storing the information about this new function in its signature.
+ */
+ if (sig == NULL) {
+ sig = new(ctx) ir_function_signature(return_type);
+ f->add_signature(sig);
+ }
+
+ sig->replace_parameters(&hir_parameters);
+ signature = sig;
+
+ /* Function declarations (prototypes) do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_function_definition::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ prototype->is_definition = true;
+ prototype->hir(instructions, state);
+
+ ir_function_signature *signature = prototype->signature;
+ if (signature == NULL)
+ return NULL;
+
+ assert(state->current_function == NULL);
+ state->current_function = signature;
+ state->found_return = false;
+
+ /* Duplicate parameters declared in the prototype as concrete variables.
+ * Add these to the symbol table.
+ */
+ state->symbols->push_scope();
+ foreach_iter(exec_list_iterator, iter, signature->parameters) {
+ ir_variable *const var = ((ir_instruction *) iter.get())->as_variable();
+
+ assert(var != NULL);
+
+ /* The only way a parameter would "exist" is if two parameters have
+ * the same name.
+ */
+ if (state->symbols->name_declared_this_scope(var->name)) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state, "parameter `%s' redeclared", var->name);
+ } else {
+ state->symbols->add_variable(var);
+ }
+ }
+
+ /* Convert the body of the function to HIR. */
+ this->body->hir(&signature->body, state);
+ signature->is_defined = true;
+
+ state->symbols->pop_scope();
+
+ assert(state->current_function == signature);
+ state->current_function = NULL;
+
+ if (!signature->return_type->is_void() && !state->found_return) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(& loc, state, "function `%s' has non-void return type "
+ "%s, but no return statement",
+ signature->function_name(),
+ signature->return_type->name);
+ }
+
+ /* Function definitions do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_jump_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+
+ switch (mode) {
+ case ast_return: {
+ ir_return *inst;
+ assert(state->current_function);
+
+ if (opt_return_value) {
+ ir_rvalue *const ret = opt_return_value->hir(instructions, state);
+
+ /* The value of the return type can be NULL if the shader says
+ * 'return foo();' and foo() is a function that returns void.
+ *
+ * NOTE: The GLSL spec doesn't say that this is an error. The type
+ * of the return value is void. If the return type of the function is
+ * also void, then this should compile without error. Seriously.
+ */
+ const glsl_type *const ret_type =
+ (ret == NULL) ? glsl_type::void_type : ret->type;
+
+ /* Implicit conversions are not allowed for return values. */
+ if (state->current_function->return_type != ret_type) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "`return' with wrong type %s, in function `%s' "
+ "returning %s",
+ ret_type->name,
+ state->current_function->function_name(),
+ state->current_function->return_type->name);
+ }
+
+ inst = new(ctx) ir_return(ret);
+ } else {
+ if (state->current_function->return_type->base_type !=
+ GLSL_TYPE_VOID) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "`return' with no value, in function %s returning "
+ "non-void",
+ state->current_function->function_name());
+ }
+ inst = new(ctx) ir_return;
+ }
+
+ state->found_return = true;
+ instructions->push_tail(inst);
+ break;
+ }
+
+ case ast_discard:
+ if (state->target != fragment_shader) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "`discard' may only appear in a fragment shader");
+ }
+ instructions->push_tail(new(ctx) ir_discard);
+ break;
+
+ case ast_break:
+ case ast_continue:
+ /* FINISHME: Handle switch-statements. They cannot contain 'continue',
+ * FINISHME: and they use a different IR instruction for 'break'.
+ */
+ /* FINISHME: Correctly handle the nesting. If a switch-statement is
+ * FINISHME: inside a loop, a 'continue' is valid and will bind to the
+ * FINISHME: loop.
+ */
+ if (state->loop_or_switch_nesting == NULL) {
+ YYLTYPE loc = this->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "`%s' may only appear in a loop",
+ (mode == ast_break) ? "break" : "continue");
+ } else {
+ ir_loop *const loop = state->loop_or_switch_nesting->as_loop();
+
+ /* Inline the for loop expression again, since we don't know
+ * where near the end of the loop body the normal copy of it
+ * is going to be placed.
+ */
+ if (mode == ast_continue &&
+ state->loop_or_switch_nesting_ast->rest_expression) {
+ state->loop_or_switch_nesting_ast->rest_expression->hir(instructions,
+ state);
+ }
+
+ if (loop != NULL) {
+ ir_loop_jump *const jump =
+ new(ctx) ir_loop_jump((mode == ast_break)
+ ? ir_loop_jump::jump_break
+ : ir_loop_jump::jump_continue);
+ instructions->push_tail(jump);
+ }
+ }
+
+ break;
+ }
+
+ /* Jump instructions do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_selection_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+
+ ir_rvalue *const condition = this->condition->hir(instructions, state);
+
+ /* From page 66 (page 72 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "Any expression whose type evaluates to a Boolean can be used as the
+ * conditional expression bool-expression. Vector types are not accepted
+ * as the expression to if."
+ *
+ * The checks are separated so that higher quality diagnostics can be
+ * generated for cases where both rules are violated.
+ */
+ if (!condition->type->is_boolean() || !condition->type->is_scalar()) {
+ YYLTYPE loc = this->condition->get_location();
+
+ _mesa_glsl_error(& loc, state, "if-statement condition must be scalar "
+ "boolean");
+ }
+
+ ir_if *const stmt = new(ctx) ir_if(condition);
+
+ if (then_statement != NULL) {
+ state->symbols->push_scope();
+ then_statement->hir(& stmt->then_instructions, state);
+ state->symbols->pop_scope();
+ }
+
+ if (else_statement != NULL) {
+ state->symbols->push_scope();
+ else_statement->hir(& stmt->else_instructions, state);
+ state->symbols->pop_scope();
+ }
+
+ instructions->push_tail(stmt);
+
+ /* if-statements do not have r-values.
+ */
+ return NULL;
+}
+
+
+void
+ast_iteration_statement::condition_to_hir(ir_loop *stmt,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+
+ if (condition != NULL) {
+ ir_rvalue *const cond =
+ condition->hir(& stmt->body_instructions, state);
+
+ if ((cond == NULL)
+ || !cond->type->is_boolean() || !cond->type->is_scalar()) {
+ YYLTYPE loc = condition->get_location();
+
+ _mesa_glsl_error(& loc, state,
+ "loop condition must be scalar boolean");
+ } else {
+ /* As the first code in the loop body, generate a block that looks
+ * like 'if (!condition) break;' as the loop termination condition.
+ */
+ ir_rvalue *const not_cond =
+ new(ctx) ir_expression(ir_unop_logic_not, glsl_type::bool_type, cond,
+ NULL);
+
+ ir_if *const if_stmt = new(ctx) ir_if(not_cond);
+
+ ir_jump *const break_stmt =
+ new(ctx) ir_loop_jump(ir_loop_jump::jump_break);
+
+ if_stmt->then_instructions.push_tail(break_stmt);
+ stmt->body_instructions.push_tail(if_stmt);
+ }
+ }
+}
+
+
+ir_rvalue *
+ast_iteration_statement::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+
+ /* For-loops and while-loops start a new scope, but do-while loops do not.
+ */
+ if (mode != ast_do_while)
+ state->symbols->push_scope();
+
+ if (init_statement != NULL)
+ init_statement->hir(instructions, state);
+
+ ir_loop *const stmt = new(ctx) ir_loop();
+ instructions->push_tail(stmt);
+
+ /* Track the current loop and / or switch-statement nesting.
+ */
+ ir_instruction *const nesting = state->loop_or_switch_nesting;
+ ast_iteration_statement *nesting_ast = state->loop_or_switch_nesting_ast;
+
+ state->loop_or_switch_nesting = stmt;
+ state->loop_or_switch_nesting_ast = this;
+
+ if (mode != ast_do_while)
+ condition_to_hir(stmt, state);
+
+ if (body != NULL)
+ body->hir(& stmt->body_instructions, state);
+
+ if (rest_expression != NULL)
+ rest_expression->hir(& stmt->body_instructions, state);
+
+ if (mode == ast_do_while)
+ condition_to_hir(stmt, state);
+
+ if (mode != ast_do_while)
+ state->symbols->pop_scope();
+
+ /* Restore previous nesting before returning.
+ */
+ state->loop_or_switch_nesting = nesting;
+ state->loop_or_switch_nesting_ast = nesting_ast;
+
+ /* Loops do not have r-values.
+ */
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_type_specifier::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ if (!this->is_precision_statement && this->structure == NULL)
+ return NULL;
+
+ YYLTYPE loc = this->get_location();
+
+ if (this->precision != ast_precision_none
+ && state->language_version != 100
+ && state->language_version < 130) {
+ _mesa_glsl_error(&loc, state,
+ "precision qualifiers exist only in "
+ "GLSL ES 1.00, and GLSL 1.30 and later");
+ return NULL;
+ }
+ if (this->precision != ast_precision_none
+ && this->structure != NULL) {
+ _mesa_glsl_error(&loc, state,
+ "precision qualifiers do not apply to structures");
+ return NULL;
+ }
+
+ /* If this is a precision statement, check that the type to which it is
+ * applied is either float or int.
+ *
+ * From section 4.5.3 of the GLSL 1.30 spec:
+ * "The precision statement
+ * precision precision-qualifier type;
+ * can be used to establish a default precision qualifier. The type
+ * field can be either int or float [...]. Any other types or
+ * qualifiers will result in an error.
+ */
+ if (this->is_precision_statement) {
+ assert(this->precision != ast_precision_none);
+ assert(this->structure == NULL); /* The check for structures was
+ * performed above. */
+ if (this->is_array) {
+ _mesa_glsl_error(&loc, state,
+ "default precision statements do not apply to "
+ "arrays");
+ return NULL;
+ }
+ if (this->type_specifier != ast_float
+ && this->type_specifier != ast_int) {
+ _mesa_glsl_error(&loc, state,
+ "default precision statements apply only to types "
+ "float and int");
+ return NULL;
+ }
+
+ /* FINISHME: Translate precision statements into IR. */
+ return NULL;
+ }
+
+ if (this->structure != NULL)
+ return this->structure->hir(instructions, state);
+
+ return NULL;
+}
+
+
+ir_rvalue *
+ast_struct_specifier::hir(exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ unsigned decl_count = 0;
+
+ /* Make an initial pass over the list of structure fields to determine how
+ * many there are. Each element in this list is an ast_declarator_list.
+ * This means that we actually need to count the number of elements in the
+ * 'declarations' list in each of the elements.
+ */
+ foreach_list_typed (ast_declarator_list, decl_list, link,
+ &this->declarations) {
+ foreach_list_const (decl_ptr, & decl_list->declarations) {
+ decl_count++;
+ }
+ }
+
+ /* Allocate storage for the structure fields and process the field
+ * declarations. As the declarations are processed, try to also convert
+ * the types to HIR. This ensures that structure definitions embedded in
+ * other structure definitions are processed.
+ */
+ glsl_struct_field *const fields = ralloc_array(state, glsl_struct_field,
+ decl_count);
+
+ unsigned i = 0;
+ foreach_list_typed (ast_declarator_list, decl_list, link,
+ &this->declarations) {
+ const char *type_name;
+
+ decl_list->type->specifier->hir(instructions, state);
+
+ /* Section 10.9 of the GLSL ES 1.00 specification states that
+ * embedded structure definitions have been removed from the language.
+ */
+ if (state->es_shader && decl_list->type->specifier->structure != NULL) {
+ YYLTYPE loc = this->get_location();
+ _mesa_glsl_error(&loc, state, "Embedded structure definitions are "
+ "not allowed in GLSL ES 1.00.");
+ }
+
+ const glsl_type *decl_type =
+ decl_list->type->specifier->glsl_type(& type_name, state);
+
+ foreach_list_typed (ast_declaration, decl, link,
+ &decl_list->declarations) {
+ const struct glsl_type *field_type = decl_type;
+ if (decl->is_array) {
+ YYLTYPE loc = decl->get_location();
+ field_type = process_array_type(&loc, decl_type, decl->array_size,
+ state);
+ }
+ fields[i].type = (field_type != NULL)
+ ? field_type : glsl_type::error_type;
+ fields[i].name = decl->identifier;
+ i++;
+ }
+ }
+
+ assert(i == decl_count);
+
+ const glsl_type *t =
+ glsl_type::get_record_instance(fields, decl_count, this->name);
+
+ YYLTYPE loc = this->get_location();
+ if (!state->symbols->add_type(name, t)) {
+ _mesa_glsl_error(& loc, state, "struct `%s' previously defined", name);
+ } else {
+ const glsl_type **s = reralloc(state, state->user_structures,
+ const glsl_type *,
+ state->num_user_structures + 1);
+ if (s != NULL) {
+ s[state->num_user_structures] = t;
+ state->user_structures = s;
+ state->num_user_structures++;
+ }
+ }
+
+ /* Structure type definitions do not have r-values.
+ */
+ return NULL;
+}
diff --git a/mesalib/src/glsl/ast_type.cpp b/mesalib/src/glsl/ast_type.cpp index c680ae5f6..8e49bef8a 100644 --- a/mesalib/src/glsl/ast_type.cpp +++ b/mesalib/src/glsl/ast_type.cpp @@ -1,138 +1,138 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "ast.h" -extern "C" { -#include "program/symbol_table.h" -} - -void -ast_type_specifier::print(void) const -{ - if (type_specifier == ast_struct) { - structure->print(); - } else { - printf("%s ", type_name); - } - - if (is_array) { - printf("[ "); - - if (array_size) { - array_size->print(); - } - - printf("] "); - } -} - -ast_type_specifier::ast_type_specifier(int specifier) - : type_specifier(ast_types(specifier)), type_name(NULL), structure(NULL), - is_array(false), array_size(NULL), precision(ast_precision_none), - is_precision_statement(false) -{ - static const char *const names[] = { - "void", - "float", - "int", - "uint", - "bool", - "vec2", - "vec3", - "vec4", - "bvec2", - "bvec3", - "bvec4", - "ivec2", - "ivec3", - "ivec4", - "uvec2", - "uvec3", - "uvec4", - "mat2", - "mat2x3", - "mat2x4", - "mat3x2", - "mat3", - "mat3x4", - "mat4x2", - "mat4x3", - "mat4", - "sampler1D", - "sampler2D", - "sampler2DRect", - "sampler3D", - "samplerCube", - "sampler1DShadow", - "sampler2DShadow", - "sampler2DRectShadow", - "samplerCubeShadow", - "sampler1DArray", - "sampler2DArray", - "sampler1DArrayShadow", - "sampler2DArrayShadow", - "isampler1D", - "isampler2D", - "isampler3D", - "isamplerCube", - "isampler1DArray", - "isampler2DArray", - "usampler1D", - "usampler2D", - "usampler3D", - "usamplerCube", - "usampler1DArray", - "usampler2DArray", - - NULL, /* ast_struct */ - NULL /* ast_type_name */ - }; - - type_name = names[specifier]; -} - -bool -ast_fully_specified_type::has_qualifiers() const -{ - return this->qualifier.flags.i != 0; -} - -bool ast_type_qualifier::has_interpolation() const -{ - return this->flags.q.smooth - || this->flags.q.flat - || this->flags.q.noperspective; -} - -const char* -ast_type_qualifier::interpolation_string() const -{ - if (this->flags.q.smooth) - return "smooth"; - else if (this->flags.q.flat) - return "flat"; - else if (this->flags.q.noperspective) - return "noperspective"; - else - return NULL; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "ast.h"
+extern "C" {
+#include "program/symbol_table.h"
+}
+
+void
+ast_type_specifier::print(void) const
+{
+ if (type_specifier == ast_struct) {
+ structure->print();
+ } else {
+ printf("%s ", type_name);
+ }
+
+ if (is_array) {
+ printf("[ ");
+
+ if (array_size) {
+ array_size->print();
+ }
+
+ printf("] ");
+ }
+}
+
+ast_type_specifier::ast_type_specifier(int specifier)
+ : type_specifier(ast_types(specifier)), type_name(NULL), structure(NULL),
+ is_array(false), array_size(NULL), precision(ast_precision_none),
+ is_precision_statement(false)
+{
+ static const char *const names[] = {
+ "void",
+ "float",
+ "int",
+ "uint",
+ "bool",
+ "vec2",
+ "vec3",
+ "vec4",
+ "bvec2",
+ "bvec3",
+ "bvec4",
+ "ivec2",
+ "ivec3",
+ "ivec4",
+ "uvec2",
+ "uvec3",
+ "uvec4",
+ "mat2",
+ "mat2x3",
+ "mat2x4",
+ "mat3x2",
+ "mat3",
+ "mat3x4",
+ "mat4x2",
+ "mat4x3",
+ "mat4",
+ "sampler1D",
+ "sampler2D",
+ "sampler2DRect",
+ "sampler3D",
+ "samplerCube",
+ "sampler1DShadow",
+ "sampler2DShadow",
+ "sampler2DRectShadow",
+ "samplerCubeShadow",
+ "sampler1DArray",
+ "sampler2DArray",
+ "sampler1DArrayShadow",
+ "sampler2DArrayShadow",
+ "isampler1D",
+ "isampler2D",
+ "isampler3D",
+ "isamplerCube",
+ "isampler1DArray",
+ "isampler2DArray",
+ "usampler1D",
+ "usampler2D",
+ "usampler3D",
+ "usamplerCube",
+ "usampler1DArray",
+ "usampler2DArray",
+
+ NULL, /* ast_struct */
+ NULL /* ast_type_name */
+ };
+
+ type_name = names[specifier];
+}
+
+bool
+ast_fully_specified_type::has_qualifiers() const
+{
+ return this->qualifier.flags.i != 0;
+}
+
+bool ast_type_qualifier::has_interpolation() const
+{
+ return this->flags.q.smooth
+ || this->flags.q.flat
+ || this->flags.q.noperspective;
+}
+
+const char*
+ast_type_qualifier::interpolation_string() const
+{
+ if (this->flags.q.smooth)
+ return "smooth";
+ else if (this->flags.q.flat)
+ return "flat";
+ else if (this->flags.q.noperspective)
+ return "noperspective";
+ else
+ return NULL;
+}
diff --git a/mesalib/src/glsl/builtin_types.h b/mesalib/src/glsl/builtin_types.h index 58b9a8127..6b98419ae 100644 --- a/mesalib/src/glsl/builtin_types.h +++ b/mesalib/src/glsl/builtin_types.h @@ -1,302 +1,302 @@ -/* - * Copyright © 2009 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -const glsl_type glsl_type::_error_type = - glsl_type(GL_INVALID_ENUM, GLSL_TYPE_ERROR, 0, 0, ""); - -const glsl_type glsl_type::_void_type = - glsl_type(GL_INVALID_ENUM, GLSL_TYPE_VOID, 0, 0, "void"); - -const glsl_type glsl_type::_sampler3D_type = - glsl_type(GL_SAMPLER_3D, GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_FLOAT, - "sampler3D"); - -const glsl_type *const glsl_type::error_type = & glsl_type::_error_type; -const glsl_type *const glsl_type::void_type = & glsl_type::_void_type; - -/** \name Core built-in types - * - * These types exist in all versions of GLSL. - */ -/*@{*/ - -const glsl_type glsl_type::builtin_core_types[] = { - glsl_type(GL_BOOL, GLSL_TYPE_BOOL, 1, 1, "bool"), - glsl_type(GL_BOOL_VEC2, GLSL_TYPE_BOOL, 2, 1, "bvec2"), - glsl_type(GL_BOOL_VEC3, GLSL_TYPE_BOOL, 3, 1, "bvec3"), - glsl_type(GL_BOOL_VEC4, GLSL_TYPE_BOOL, 4, 1, "bvec4"), - glsl_type(GL_INT, GLSL_TYPE_INT, 1, 1, "int"), - glsl_type(GL_INT_VEC2, GLSL_TYPE_INT, 2, 1, "ivec2"), - glsl_type(GL_INT_VEC3, GLSL_TYPE_INT, 3, 1, "ivec3"), - glsl_type(GL_INT_VEC4, GLSL_TYPE_INT, 4, 1, "ivec4"), - glsl_type(GL_FLOAT, GLSL_TYPE_FLOAT, 1, 1, "float"), - glsl_type(GL_FLOAT_VEC2, GLSL_TYPE_FLOAT, 2, 1, "vec2"), - glsl_type(GL_FLOAT_VEC3, GLSL_TYPE_FLOAT, 3, 1, "vec3"), - glsl_type(GL_FLOAT_VEC4, GLSL_TYPE_FLOAT, 4, 1, "vec4"), - glsl_type(GL_FLOAT_MAT2, GLSL_TYPE_FLOAT, 2, 2, "mat2"), - glsl_type(GL_FLOAT_MAT3, GLSL_TYPE_FLOAT, 3, 3, "mat3"), - glsl_type(GL_FLOAT_MAT4, GLSL_TYPE_FLOAT, 4, 4, "mat4"), - glsl_type(GL_SAMPLER_2D, GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_FLOAT, - "sampler2D"), - glsl_type(GL_SAMPLER_CUBE, GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_FLOAT, - "samplerCube"), -}; - -const glsl_type *const glsl_type::bool_type = & builtin_core_types[0]; -const glsl_type *const glsl_type::int_type = & builtin_core_types[4]; -const glsl_type *const glsl_type::ivec4_type = & builtin_core_types[7]; -const glsl_type *const glsl_type::float_type = & builtin_core_types[8]; -const glsl_type *const glsl_type::vec2_type = & builtin_core_types[9]; -const glsl_type *const glsl_type::vec3_type = & builtin_core_types[10]; -const glsl_type *const glsl_type::vec4_type = & builtin_core_types[11]; -const glsl_type *const glsl_type::mat2_type = & builtin_core_types[12]; -const glsl_type *const glsl_type::mat3_type = & builtin_core_types[13]; -const glsl_type *const glsl_type::mat4_type = & builtin_core_types[14]; -/*@}*/ - -/** \name GLSL structures that have not been deprecated. - */ -/*@{*/ - -static const struct glsl_struct_field gl_DepthRangeParameters_fields[] = { - { glsl_type::float_type, "near" }, - { glsl_type::float_type, "far" }, - { glsl_type::float_type, "diff" }, -}; - -const glsl_type glsl_type::builtin_structure_types[] = { - glsl_type(gl_DepthRangeParameters_fields, - Elements(gl_DepthRangeParameters_fields), - "gl_DepthRangeParameters"), -}; -/*@}*/ - -/** \name GLSL 1.00 / 1.10 structures that are deprecated in GLSL 1.30 - */ -/*@{*/ - -static const struct glsl_struct_field gl_PointParameters_fields[] = { - { glsl_type::float_type, "size" }, - { glsl_type::float_type, "sizeMin" }, - { glsl_type::float_type, "sizeMax" }, - { glsl_type::float_type, "fadeThresholdSize" }, - { glsl_type::float_type, "distanceConstantAttenuation" }, - { glsl_type::float_type, "distanceLinearAttenuation" }, - { glsl_type::float_type, "distanceQuadraticAttenuation" }, -}; - -static const struct glsl_struct_field gl_MaterialParameters_fields[] = { - { glsl_type::vec4_type, "emission" }, - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, - { glsl_type::float_type, "shininess" }, -}; - -static const struct glsl_struct_field gl_LightSourceParameters_fields[] = { - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, - { glsl_type::vec4_type, "position" }, - { glsl_type::vec4_type, "halfVector" }, - { glsl_type::vec3_type, "spotDirection" }, - { glsl_type::float_type, "spotExponent" }, - { glsl_type::float_type, "spotCutoff" }, - { glsl_type::float_type, "spotCosCutoff" }, - { glsl_type::float_type, "constantAttenuation" }, - { glsl_type::float_type, "linearAttenuation" }, - { glsl_type::float_type, "quadraticAttenuation" }, -}; - -static const struct glsl_struct_field gl_LightModelParameters_fields[] = { - { glsl_type::vec4_type, "ambient" }, -}; - -static const struct glsl_struct_field gl_LightModelProducts_fields[] = { - { glsl_type::vec4_type, "sceneColor" }, -}; - -static const struct glsl_struct_field gl_LightProducts_fields[] = { - { glsl_type::vec4_type, "ambient" }, - { glsl_type::vec4_type, "diffuse" }, - { glsl_type::vec4_type, "specular" }, -}; - -static const struct glsl_struct_field gl_FogParameters_fields[] = { - { glsl_type::vec4_type, "color" }, - { glsl_type::float_type, "density" }, - { glsl_type::float_type, "start" }, - { glsl_type::float_type, "end" }, - { glsl_type::float_type, "scale" }, -}; - -const glsl_type glsl_type::builtin_110_deprecated_structure_types[] = { - glsl_type(gl_PointParameters_fields, - Elements(gl_PointParameters_fields), - "gl_PointParameters"), - glsl_type(gl_MaterialParameters_fields, - Elements(gl_MaterialParameters_fields), - "gl_MaterialParameters"), - glsl_type(gl_LightSourceParameters_fields, - Elements(gl_LightSourceParameters_fields), - "gl_LightSourceParameters"), - glsl_type(gl_LightModelParameters_fields, - Elements(gl_LightModelParameters_fields), - "gl_LightModelParameters"), - glsl_type(gl_LightModelProducts_fields, - Elements(gl_LightModelProducts_fields), - "gl_LightModelProducts"), - glsl_type(gl_LightProducts_fields, - Elements(gl_LightProducts_fields), - "gl_LightProducts"), - glsl_type(gl_FogParameters_fields, - Elements(gl_FogParameters_fields), - "gl_FogParameters"), -}; -/*@}*/ - -/** \name Types in GLSL 1.10 (but not GLSL ES 1.00) - */ -/*@{*/ -const glsl_type glsl_type::builtin_110_types[] = { - glsl_type(GL_SAMPLER_1D, GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_FLOAT, - "sampler1D"), - glsl_type(GL_SAMPLER_1D_SHADOW, GLSL_SAMPLER_DIM_1D, 1, 0, GLSL_TYPE_FLOAT, - "sampler1DShadow"), - glsl_type(GL_SAMPLER_2D_SHADOW, GLSL_SAMPLER_DIM_2D, 1, 0, GLSL_TYPE_FLOAT, - "sampler2DShadow"), -}; -/*@}*/ - -/** \name Types added in GLSL 1.20 - */ -/*@{*/ - -const glsl_type glsl_type::builtin_120_types[] = { - glsl_type(GL_FLOAT_MAT2x3, GLSL_TYPE_FLOAT, 3, 2, "mat2x3"), - glsl_type(GL_FLOAT_MAT2x4, GLSL_TYPE_FLOAT, 4, 2, "mat2x4"), - glsl_type(GL_FLOAT_MAT3x2, GLSL_TYPE_FLOAT, 2, 3, "mat3x2"), - glsl_type(GL_FLOAT_MAT3x4, GLSL_TYPE_FLOAT, 4, 3, "mat3x4"), - glsl_type(GL_FLOAT_MAT4x2, GLSL_TYPE_FLOAT, 2, 4, "mat4x2"), - glsl_type(GL_FLOAT_MAT4x3, GLSL_TYPE_FLOAT, 3, 4, "mat4x3"), -}; -const glsl_type *const glsl_type::mat2x3_type = & builtin_120_types[0]; -const glsl_type *const glsl_type::mat2x4_type = & builtin_120_types[1]; -const glsl_type *const glsl_type::mat3x2_type = & builtin_120_types[2]; -const glsl_type *const glsl_type::mat3x4_type = & builtin_120_types[3]; -const glsl_type *const glsl_type::mat4x2_type = & builtin_120_types[4]; -const glsl_type *const glsl_type::mat4x3_type = & builtin_120_types[5]; -/*@}*/ - -/** \name Types added in GLSL 1.30 - */ -/*@{*/ - -const glsl_type glsl_type::builtin_130_types[] = { - glsl_type(GL_UNSIGNED_INT, GLSL_TYPE_UINT, 1, 1, "uint"), - glsl_type(GL_UNSIGNED_INT_VEC2, GLSL_TYPE_UINT, 2, 1, "uvec2"), - glsl_type(GL_UNSIGNED_INT_VEC3, GLSL_TYPE_UINT, 3, 1, "uvec3"), - glsl_type(GL_UNSIGNED_INT_VEC4, GLSL_TYPE_UINT, 4, 1, "uvec4"), - - /* 1D and 2D texture arrays - several of these are included only in - * builtin_EXT_texture_array_types. - */ - glsl_type(GL_INT_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_INT, "isampler1DArray"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_UINT, "usampler1DArray"), - glsl_type(GL_INT_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_INT, "isampler2DArray"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_UINT, "usampler2DArray"), - - /* cube shadow samplers */ - glsl_type(GL_SAMPLER_CUBE_SHADOW, - GLSL_SAMPLER_DIM_CUBE, 1, 0, GLSL_TYPE_FLOAT, "samplerCubeShadow"), - - /* signed and unsigned integer samplers */ - glsl_type(GL_INT_SAMPLER_1D, - GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_INT, "isampler1D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_1D, - GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_UINT, "usampler1D"), - glsl_type(GL_INT_SAMPLER_2D, - GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_INT, "isampler2D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_2D, - GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_UINT, "usampler2D"), - glsl_type(GL_INT_SAMPLER_3D, - GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_INT, "isampler3D"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_3D, - GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_UINT, "usampler3D"), - glsl_type(GL_INT_SAMPLER_CUBE, - GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_INT, "isamplerCube"), - glsl_type(GL_INT_SAMPLER_CUBE, - GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_UINT, "usamplerCube"), -}; - -const glsl_type *const glsl_type::uint_type = & builtin_130_types[0]; -const glsl_type *const glsl_type::uvec2_type = & builtin_130_types[1]; -const glsl_type *const glsl_type::uvec3_type = & builtin_130_types[2]; -const glsl_type *const glsl_type::uvec4_type = & builtin_130_types[3]; -/*@}*/ - -/** \name Sampler types added by GL_ARB_texture_rectangle - */ -/*@{*/ - -const glsl_type glsl_type::builtin_ARB_texture_rectangle_types[] = { - glsl_type(GL_SAMPLER_2D_RECT, - GLSL_SAMPLER_DIM_RECT, 0, 0, GLSL_TYPE_FLOAT, "sampler2DRect"), - glsl_type(GL_SAMPLER_2D_RECT_SHADOW, - GLSL_SAMPLER_DIM_RECT, 1, 0, GLSL_TYPE_FLOAT, "sampler2DRectShadow"), -}; -/*@}*/ - -/** \name Sampler types added by GL_EXT_texture_array - */ -/*@{*/ - -const glsl_type glsl_type::builtin_EXT_texture_array_types[] = { - glsl_type(GL_SAMPLER_1D_ARRAY, - GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_FLOAT, "sampler1DArray"), - glsl_type(GL_SAMPLER_2D_ARRAY, - GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_FLOAT, "sampler2DArray"), - glsl_type(GL_SAMPLER_1D_ARRAY_SHADOW, - GLSL_SAMPLER_DIM_1D, 1, 1, GLSL_TYPE_FLOAT, "sampler1DArrayShadow"), - glsl_type(GL_SAMPLER_2D_ARRAY_SHADOW, - GLSL_SAMPLER_DIM_2D, 1, 1, GLSL_TYPE_FLOAT, "sampler2DArrayShadow"), -}; -/*@}*/ - -/** \name Sampler types added by GL_EXT_texture_buffer_object - */ -/*@{*/ - -const glsl_type glsl_type::builtin_EXT_texture_buffer_object_types[] = { - glsl_type(GL_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_FLOAT, "samplerBuffer"), - glsl_type(GL_INT_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_INT, "isamplerBuffer"), - glsl_type(GL_UNSIGNED_INT_SAMPLER_BUFFER, - GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_UINT, "usamplerBuffer"), -}; -/*@}*/ +/*
+ * Copyright © 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+const glsl_type glsl_type::_error_type =
+ glsl_type(GL_INVALID_ENUM, GLSL_TYPE_ERROR, 0, 0, "");
+
+const glsl_type glsl_type::_void_type =
+ glsl_type(GL_INVALID_ENUM, GLSL_TYPE_VOID, 0, 0, "void");
+
+const glsl_type glsl_type::_sampler3D_type =
+ glsl_type(GL_SAMPLER_3D, GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_FLOAT,
+ "sampler3D");
+
+const glsl_type *const glsl_type::error_type = & glsl_type::_error_type;
+const glsl_type *const glsl_type::void_type = & glsl_type::_void_type;
+
+/** \name Core built-in types
+ *
+ * These types exist in all versions of GLSL.
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_core_types[] = {
+ glsl_type(GL_BOOL, GLSL_TYPE_BOOL, 1, 1, "bool"),
+ glsl_type(GL_BOOL_VEC2, GLSL_TYPE_BOOL, 2, 1, "bvec2"),
+ glsl_type(GL_BOOL_VEC3, GLSL_TYPE_BOOL, 3, 1, "bvec3"),
+ glsl_type(GL_BOOL_VEC4, GLSL_TYPE_BOOL, 4, 1, "bvec4"),
+ glsl_type(GL_INT, GLSL_TYPE_INT, 1, 1, "int"),
+ glsl_type(GL_INT_VEC2, GLSL_TYPE_INT, 2, 1, "ivec2"),
+ glsl_type(GL_INT_VEC3, GLSL_TYPE_INT, 3, 1, "ivec3"),
+ glsl_type(GL_INT_VEC4, GLSL_TYPE_INT, 4, 1, "ivec4"),
+ glsl_type(GL_FLOAT, GLSL_TYPE_FLOAT, 1, 1, "float"),
+ glsl_type(GL_FLOAT_VEC2, GLSL_TYPE_FLOAT, 2, 1, "vec2"),
+ glsl_type(GL_FLOAT_VEC3, GLSL_TYPE_FLOAT, 3, 1, "vec3"),
+ glsl_type(GL_FLOAT_VEC4, GLSL_TYPE_FLOAT, 4, 1, "vec4"),
+ glsl_type(GL_FLOAT_MAT2, GLSL_TYPE_FLOAT, 2, 2, "mat2"),
+ glsl_type(GL_FLOAT_MAT3, GLSL_TYPE_FLOAT, 3, 3, "mat3"),
+ glsl_type(GL_FLOAT_MAT4, GLSL_TYPE_FLOAT, 4, 4, "mat4"),
+ glsl_type(GL_SAMPLER_2D, GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_FLOAT,
+ "sampler2D"),
+ glsl_type(GL_SAMPLER_CUBE, GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_FLOAT,
+ "samplerCube"),
+};
+
+const glsl_type *const glsl_type::bool_type = & builtin_core_types[0];
+const glsl_type *const glsl_type::int_type = & builtin_core_types[4];
+const glsl_type *const glsl_type::ivec4_type = & builtin_core_types[7];
+const glsl_type *const glsl_type::float_type = & builtin_core_types[8];
+const glsl_type *const glsl_type::vec2_type = & builtin_core_types[9];
+const glsl_type *const glsl_type::vec3_type = & builtin_core_types[10];
+const glsl_type *const glsl_type::vec4_type = & builtin_core_types[11];
+const glsl_type *const glsl_type::mat2_type = & builtin_core_types[12];
+const glsl_type *const glsl_type::mat3_type = & builtin_core_types[13];
+const glsl_type *const glsl_type::mat4_type = & builtin_core_types[14];
+/*@}*/
+
+/** \name GLSL structures that have not been deprecated.
+ */
+/*@{*/
+
+static const struct glsl_struct_field gl_DepthRangeParameters_fields[] = {
+ { glsl_type::float_type, "near" },
+ { glsl_type::float_type, "far" },
+ { glsl_type::float_type, "diff" },
+};
+
+const glsl_type glsl_type::builtin_structure_types[] = {
+ glsl_type(gl_DepthRangeParameters_fields,
+ Elements(gl_DepthRangeParameters_fields),
+ "gl_DepthRangeParameters"),
+};
+/*@}*/
+
+/** \name GLSL 1.00 / 1.10 structures that are deprecated in GLSL 1.30
+ */
+/*@{*/
+
+static const struct glsl_struct_field gl_PointParameters_fields[] = {
+ { glsl_type::float_type, "size" },
+ { glsl_type::float_type, "sizeMin" },
+ { glsl_type::float_type, "sizeMax" },
+ { glsl_type::float_type, "fadeThresholdSize" },
+ { glsl_type::float_type, "distanceConstantAttenuation" },
+ { glsl_type::float_type, "distanceLinearAttenuation" },
+ { glsl_type::float_type, "distanceQuadraticAttenuation" },
+};
+
+static const struct glsl_struct_field gl_MaterialParameters_fields[] = {
+ { glsl_type::vec4_type, "emission" },
+ { glsl_type::vec4_type, "ambient" },
+ { glsl_type::vec4_type, "diffuse" },
+ { glsl_type::vec4_type, "specular" },
+ { glsl_type::float_type, "shininess" },
+};
+
+static const struct glsl_struct_field gl_LightSourceParameters_fields[] = {
+ { glsl_type::vec4_type, "ambient" },
+ { glsl_type::vec4_type, "diffuse" },
+ { glsl_type::vec4_type, "specular" },
+ { glsl_type::vec4_type, "position" },
+ { glsl_type::vec4_type, "halfVector" },
+ { glsl_type::vec3_type, "spotDirection" },
+ { glsl_type::float_type, "spotExponent" },
+ { glsl_type::float_type, "spotCutoff" },
+ { glsl_type::float_type, "spotCosCutoff" },
+ { glsl_type::float_type, "constantAttenuation" },
+ { glsl_type::float_type, "linearAttenuation" },
+ { glsl_type::float_type, "quadraticAttenuation" },
+};
+
+static const struct glsl_struct_field gl_LightModelParameters_fields[] = {
+ { glsl_type::vec4_type, "ambient" },
+};
+
+static const struct glsl_struct_field gl_LightModelProducts_fields[] = {
+ { glsl_type::vec4_type, "sceneColor" },
+};
+
+static const struct glsl_struct_field gl_LightProducts_fields[] = {
+ { glsl_type::vec4_type, "ambient" },
+ { glsl_type::vec4_type, "diffuse" },
+ { glsl_type::vec4_type, "specular" },
+};
+
+static const struct glsl_struct_field gl_FogParameters_fields[] = {
+ { glsl_type::vec4_type, "color" },
+ { glsl_type::float_type, "density" },
+ { glsl_type::float_type, "start" },
+ { glsl_type::float_type, "end" },
+ { glsl_type::float_type, "scale" },
+};
+
+const glsl_type glsl_type::builtin_110_deprecated_structure_types[] = {
+ glsl_type(gl_PointParameters_fields,
+ Elements(gl_PointParameters_fields),
+ "gl_PointParameters"),
+ glsl_type(gl_MaterialParameters_fields,
+ Elements(gl_MaterialParameters_fields),
+ "gl_MaterialParameters"),
+ glsl_type(gl_LightSourceParameters_fields,
+ Elements(gl_LightSourceParameters_fields),
+ "gl_LightSourceParameters"),
+ glsl_type(gl_LightModelParameters_fields,
+ Elements(gl_LightModelParameters_fields),
+ "gl_LightModelParameters"),
+ glsl_type(gl_LightModelProducts_fields,
+ Elements(gl_LightModelProducts_fields),
+ "gl_LightModelProducts"),
+ glsl_type(gl_LightProducts_fields,
+ Elements(gl_LightProducts_fields),
+ "gl_LightProducts"),
+ glsl_type(gl_FogParameters_fields,
+ Elements(gl_FogParameters_fields),
+ "gl_FogParameters"),
+};
+/*@}*/
+
+/** \name Types in GLSL 1.10 (but not GLSL ES 1.00)
+ */
+/*@{*/
+const glsl_type glsl_type::builtin_110_types[] = {
+ glsl_type(GL_SAMPLER_1D, GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_FLOAT,
+ "sampler1D"),
+ glsl_type(GL_SAMPLER_1D_SHADOW, GLSL_SAMPLER_DIM_1D, 1, 0, GLSL_TYPE_FLOAT,
+ "sampler1DShadow"),
+ glsl_type(GL_SAMPLER_2D_SHADOW, GLSL_SAMPLER_DIM_2D, 1, 0, GLSL_TYPE_FLOAT,
+ "sampler2DShadow"),
+};
+/*@}*/
+
+/** \name Types added in GLSL 1.20
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_120_types[] = {
+ glsl_type(GL_FLOAT_MAT2x3, GLSL_TYPE_FLOAT, 3, 2, "mat2x3"),
+ glsl_type(GL_FLOAT_MAT2x4, GLSL_TYPE_FLOAT, 4, 2, "mat2x4"),
+ glsl_type(GL_FLOAT_MAT3x2, GLSL_TYPE_FLOAT, 2, 3, "mat3x2"),
+ glsl_type(GL_FLOAT_MAT3x4, GLSL_TYPE_FLOAT, 4, 3, "mat3x4"),
+ glsl_type(GL_FLOAT_MAT4x2, GLSL_TYPE_FLOAT, 2, 4, "mat4x2"),
+ glsl_type(GL_FLOAT_MAT4x3, GLSL_TYPE_FLOAT, 3, 4, "mat4x3"),
+};
+const glsl_type *const glsl_type::mat2x3_type = & builtin_120_types[0];
+const glsl_type *const glsl_type::mat2x4_type = & builtin_120_types[1];
+const glsl_type *const glsl_type::mat3x2_type = & builtin_120_types[2];
+const glsl_type *const glsl_type::mat3x4_type = & builtin_120_types[3];
+const glsl_type *const glsl_type::mat4x2_type = & builtin_120_types[4];
+const glsl_type *const glsl_type::mat4x3_type = & builtin_120_types[5];
+/*@}*/
+
+/** \name Types added in GLSL 1.30
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_130_types[] = {
+ glsl_type(GL_UNSIGNED_INT, GLSL_TYPE_UINT, 1, 1, "uint"),
+ glsl_type(GL_UNSIGNED_INT_VEC2, GLSL_TYPE_UINT, 2, 1, "uvec2"),
+ glsl_type(GL_UNSIGNED_INT_VEC3, GLSL_TYPE_UINT, 3, 1, "uvec3"),
+ glsl_type(GL_UNSIGNED_INT_VEC4, GLSL_TYPE_UINT, 4, 1, "uvec4"),
+
+ /* 1D and 2D texture arrays - several of these are included only in
+ * builtin_EXT_texture_array_types.
+ */
+ glsl_type(GL_INT_SAMPLER_1D_ARRAY,
+ GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_INT, "isampler1DArray"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY,
+ GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_UINT, "usampler1DArray"),
+ glsl_type(GL_INT_SAMPLER_2D_ARRAY,
+ GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_INT, "isampler2DArray"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY,
+ GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_UINT, "usampler2DArray"),
+
+ /* cube shadow samplers */
+ glsl_type(GL_SAMPLER_CUBE_SHADOW,
+ GLSL_SAMPLER_DIM_CUBE, 1, 0, GLSL_TYPE_FLOAT, "samplerCubeShadow"),
+
+ /* signed and unsigned integer samplers */
+ glsl_type(GL_INT_SAMPLER_1D,
+ GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_INT, "isampler1D"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_1D,
+ GLSL_SAMPLER_DIM_1D, 0, 0, GLSL_TYPE_UINT, "usampler1D"),
+ glsl_type(GL_INT_SAMPLER_2D,
+ GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_INT, "isampler2D"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_2D,
+ GLSL_SAMPLER_DIM_2D, 0, 0, GLSL_TYPE_UINT, "usampler2D"),
+ glsl_type(GL_INT_SAMPLER_3D,
+ GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_INT, "isampler3D"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_3D,
+ GLSL_SAMPLER_DIM_3D, 0, 0, GLSL_TYPE_UINT, "usampler3D"),
+ glsl_type(GL_INT_SAMPLER_CUBE,
+ GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_INT, "isamplerCube"),
+ glsl_type(GL_INT_SAMPLER_CUBE,
+ GLSL_SAMPLER_DIM_CUBE, 0, 0, GLSL_TYPE_UINT, "usamplerCube"),
+};
+
+const glsl_type *const glsl_type::uint_type = & builtin_130_types[0];
+const glsl_type *const glsl_type::uvec2_type = & builtin_130_types[1];
+const glsl_type *const glsl_type::uvec3_type = & builtin_130_types[2];
+const glsl_type *const glsl_type::uvec4_type = & builtin_130_types[3];
+/*@}*/
+
+/** \name Sampler types added by GL_ARB_texture_rectangle
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_ARB_texture_rectangle_types[] = {
+ glsl_type(GL_SAMPLER_2D_RECT,
+ GLSL_SAMPLER_DIM_RECT, 0, 0, GLSL_TYPE_FLOAT, "sampler2DRect"),
+ glsl_type(GL_SAMPLER_2D_RECT_SHADOW,
+ GLSL_SAMPLER_DIM_RECT, 1, 0, GLSL_TYPE_FLOAT, "sampler2DRectShadow"),
+};
+/*@}*/
+
+/** \name Sampler types added by GL_EXT_texture_array
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_EXT_texture_array_types[] = {
+ glsl_type(GL_SAMPLER_1D_ARRAY,
+ GLSL_SAMPLER_DIM_1D, 0, 1, GLSL_TYPE_FLOAT, "sampler1DArray"),
+ glsl_type(GL_SAMPLER_2D_ARRAY,
+ GLSL_SAMPLER_DIM_2D, 0, 1, GLSL_TYPE_FLOAT, "sampler2DArray"),
+ glsl_type(GL_SAMPLER_1D_ARRAY_SHADOW,
+ GLSL_SAMPLER_DIM_1D, 1, 1, GLSL_TYPE_FLOAT, "sampler1DArrayShadow"),
+ glsl_type(GL_SAMPLER_2D_ARRAY_SHADOW,
+ GLSL_SAMPLER_DIM_2D, 1, 1, GLSL_TYPE_FLOAT, "sampler2DArrayShadow"),
+};
+/*@}*/
+
+/** \name Sampler types added by GL_EXT_texture_buffer_object
+ */
+/*@{*/
+
+const glsl_type glsl_type::builtin_EXT_texture_buffer_object_types[] = {
+ glsl_type(GL_SAMPLER_BUFFER,
+ GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_FLOAT, "samplerBuffer"),
+ glsl_type(GL_INT_SAMPLER_BUFFER,
+ GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_INT, "isamplerBuffer"),
+ glsl_type(GL_UNSIGNED_INT_SAMPLER_BUFFER,
+ GLSL_SAMPLER_DIM_BUF, 0, 0, GLSL_TYPE_UINT, "usamplerBuffer"),
+};
+/*@}*/
diff --git a/mesalib/src/glsl/doflexbison.bat b/mesalib/src/glsl/doflexbison.bat new file mode 100644 index 000000000..8a5e4f0a6 --- /dev/null +++ b/mesalib/src/glsl/doflexbison.bat @@ -0,0 +1,21 @@ +@echo off
+setlocal
+
+cd "%~dp0"
+
+set M4=..\..\..\tools\mhmake\m4.exe
+set BISON_PKGDATADIR=../../../tools/mhmake/src/bisondata
+
+set path=..\..\..\tools\mhmake;%path%
+
+..\..\..\tools\mhmake\bison.exe -v -o glsl_parser.cpp -p "_mesa_glsl_" --defines=glsl_parser.h glsl_parser.yy
+
+..\..\..\tools\mhmake\bison.exe -v -o glcpp/glcpp-parse.c --defines=glcpp/glcpp-parse.h glcpp/glcpp-parse.y
+
+copy "..\..\..\tools\mhmake\flex++.exe" flex.exe
+flex.exe --nounistd -oglsl_lexer.cpp glsl_lexer.ll
+flex.exe --nounistd -oglcpp/glcpp-lex.c glcpp/glcpp-lex.l
+del flex.exe
+
+endlocal
+
diff --git a/mesalib/src/glsl/getopt.cpp b/mesalib/src/glsl/getopt.cpp new file mode 100644 index 000000000..ea5dd3927 --- /dev/null +++ b/mesalib/src/glsl/getopt.cpp @@ -0,0 +1,552 @@ +/* $OpenBSD: getopt_long.c,v 1.20 2005/10/25 15:49:37 jmc Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ +// Adapted for Box Backup by Chris Wilson <chris+boxbackup@qwirx.com> + +/* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +// #include "Box.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define __STDC__ 1 + +#include "getopt.h" + +#if defined _MSC_VER || defined __MINGW32__ +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static void warnx(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } + + static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); + static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); + static int gcd(int, int); + static void permute_args(int, int, int, char * const *); + + static char *place = EMSG; /* option letter processing */ + + /* XXX: set optreset to 1 rather than these two */ + static int nonopt_start = -1; /* first non option argument (for permute) */ + static int nonopt_end = -1; /* first option after non options (for permute) */ + + /* Error messages */ + static const char recargchar[] = "option requires an argument -- %c"; + static const char recargstring[] = "option requires an argument -- %s"; + static const char ambig[] = "ambiguous option -- %.*s"; + static const char noarg[] = "option doesn't take an argument -- %.*s"; + static const char illoptchar[] = "unknown option -- %c"; + static const char illoptstring[] = "unknown option -- %s"; + + /* + * Compute the greatest common divisor of a and b. + */ + static int + gcd(int a, int b) + { + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); + } + + /* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ + static void + permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) + { + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } + } + + /* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ + static int + parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) + { + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); + } + + /* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ + static int + getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) + { + const char * oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + else if (*options == '-') + flags |= FLAG_ALLARGS; + if (*options == '+' || *options == '-') + options++; + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; + start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + optchar == (int)'-' && *place != '\0' || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + /* XXX: disable test for :: if PC? (GNU doesn't) */ + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } else if (!(flags & FLAG_PERMUTE)) { + /* + * If permutation is disabled, we can accept an + * optional arg separated by whitespace so long + * as it does not start with a dash (-). + */ + if (optind + 1 < nargc && *nargv[optind + 1] != '-') + optarg = nargv[++optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); + } + + #ifdef REPLACE_GETOPT + /* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ + int + getopt(int nargc, char * const *nargv, const char *options) + { + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); + } + #endif /* REPLACE_GETOPT */ + + /* + * getopt_long -- + * Parse argc/argv argument vector. + */ + int + getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) + { + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); + } + + /* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ + int + getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) + { + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); + } + +#endif // defined _MSC_VER || defined __MINGW32__
\ No newline at end of file diff --git a/mesalib/src/glsl/glcpp/glcpp.c b/mesalib/src/glsl/glcpp/glcpp.c index 63eb061c2..fd602c1fb 100644 --- a/mesalib/src/glsl/glcpp/glcpp.c +++ b/mesalib/src/glsl/glcpp/glcpp.c @@ -28,6 +28,14 @@ #include "main/mtypes.h"
#include "main/shaderobj.h"
+#ifdef _MSC_VER
+#include <io.h>
+#define STDIN_FILENO 0
+#define read _read
+#define open _open
+#define close _close
+#endif
+
extern int yydebug;
void
diff --git a/mesalib/src/glsl/glsl_parser_extras.cpp b/mesalib/src/glsl/glsl_parser_extras.cpp index e8c60936f..f18ec2ae2 100644 --- a/mesalib/src/glsl/glsl_parser_extras.cpp +++ b/mesalib/src/glsl/glsl_parser_extras.cpp @@ -1,831 +1,831 @@ -/* - * Copyright © 2008, 2009 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <assert.h> - -extern "C" { -#include "main/core.h" /* for struct gl_context */ -} - -#include "ralloc.h" -#include "ast.h" -#include "glsl_parser_extras.h" -#include "glsl_parser.h" -#include "ir_optimization.h" -#include "loop_analysis.h" - -_mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx, - GLenum target, void *mem_ctx) -{ - switch (target) { - case GL_VERTEX_SHADER: this->target = vertex_shader; break; - case GL_FRAGMENT_SHADER: this->target = fragment_shader; break; - case GL_GEOMETRY_SHADER: this->target = geometry_shader; break; - } - - this->scanner = NULL; - this->translation_unit.make_empty(); - this->symbols = new(mem_ctx) glsl_symbol_table; - this->info_log = ralloc_strdup(mem_ctx, ""); - this->error = false; - this->loop_or_switch_nesting = NULL; - - /* Set default language version and extensions */ - this->language_version = 110; - this->es_shader = false; - this->ARB_texture_rectangle_enable = true; - - /* OpenGL ES 2.0 has different defaults from desktop GL. */ - if (ctx->API == API_OPENGLES2) { - this->language_version = 100; - this->es_shader = true; - this->ARB_texture_rectangle_enable = false; - } - - this->extensions = &ctx->Extensions; - - this->Const.MaxLights = ctx->Const.MaxLights; - this->Const.MaxClipPlanes = ctx->Const.MaxClipPlanes; - this->Const.MaxTextureUnits = ctx->Const.MaxTextureUnits; - this->Const.MaxTextureCoords = ctx->Const.MaxTextureCoordUnits; - this->Const.MaxVertexAttribs = ctx->Const.VertexProgram.MaxAttribs; - this->Const.MaxVertexUniformComponents = ctx->Const.VertexProgram.MaxUniformComponents; - this->Const.MaxVaryingFloats = ctx->Const.MaxVarying * 4; - this->Const.MaxVertexTextureImageUnits = ctx->Const.MaxVertexTextureImageUnits; - this->Const.MaxCombinedTextureImageUnits = ctx->Const.MaxCombinedTextureImageUnits; - this->Const.MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits; - this->Const.MaxFragmentUniformComponents = ctx->Const.FragmentProgram.MaxUniformComponents; - - this->Const.MaxDrawBuffers = ctx->Const.MaxDrawBuffers; - - /* Note: Once the OpenGL 3.0 'forward compatible' context or the OpenGL 3.2 - * Core context is supported, this logic will need change. Older versions of - * GLSL are no longer supported outside the compatibility contexts of 3.x. - */ - this->Const.GLSL_100ES = (ctx->API == API_OPENGLES2) - || ctx->Extensions.ARB_ES2_compatibility; - this->Const.GLSL_110 = (ctx->API == API_OPENGL); - this->Const.GLSL_120 = (ctx->API == API_OPENGL) - && (ctx->Const.GLSLVersion >= 120); - this->Const.GLSL_130 = (ctx->API == API_OPENGL) - && (ctx->Const.GLSLVersion >= 130); - - const unsigned lowest_version = - (ctx->API == API_OPENGLES2) || ctx->Extensions.ARB_ES2_compatibility - ? 100 : 110; - const unsigned highest_version = - (ctx->API == API_OPENGL) ? ctx->Const.GLSLVersion : 100; - char *supported = ralloc_strdup(this, ""); - - for (unsigned ver = lowest_version; ver <= highest_version; ver += 10) { - const char *const prefix = (ver == lowest_version) - ? "" - : ((ver == highest_version) ? ", and " : ", "); - - ralloc_asprintf_append(& supported, "%s%d.%02d%s", - prefix, - ver / 100, ver % 100, - (ver == 100) ? " ES" : ""); - } - - this->supported_version_string = supported; -} - -const char * -_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target) -{ - switch (target) { - case vertex_shader: return "vertex"; - case fragment_shader: return "fragment"; - case geometry_shader: return "geometry"; - } - - assert(!"Should not get here."); - return "unknown"; -} - - -void -_mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...) -{ - va_list ap; - - state->error = true; - - assert(state->info_log != NULL); - ralloc_asprintf_append(&state->info_log, "%u:%u(%u): error: ", - locp->source, - locp->first_line, - locp->first_column); - va_start(ap, fmt); - ralloc_vasprintf_append(&state->info_log, fmt, ap); - va_end(ap); - ralloc_strcat(&state->info_log, "\n"); -} - - -void -_mesa_glsl_warning(const YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...) -{ - va_list ap; - - assert(state->info_log != NULL); - ralloc_asprintf_append(&state->info_log, "%u:%u(%u): warning: ", - locp->source, - locp->first_line, - locp->first_column); - va_start(ap, fmt); - ralloc_vasprintf_append(&state->info_log, fmt, ap); - va_end(ap); - ralloc_strcat(&state->info_log, "\n"); -} - - -bool -_mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, - const char *behavior, YYLTYPE *behavior_locp, - _mesa_glsl_parse_state *state) -{ - enum { - extension_disable, - extension_enable, - extension_require, - extension_warn - } ext_mode; - - if (strcmp(behavior, "warn") == 0) { - ext_mode = extension_warn; - } else if (strcmp(behavior, "require") == 0) { - ext_mode = extension_require; - } else if (strcmp(behavior, "enable") == 0) { - ext_mode = extension_enable; - } else if (strcmp(behavior, "disable") == 0) { - ext_mode = extension_disable; - } else { - _mesa_glsl_error(behavior_locp, state, - "Unknown extension behavior `%s'", - behavior); - return false; - } - - bool unsupported = false; - - if (strcmp(name, "all") == 0) { - if ((ext_mode == extension_enable) || (ext_mode == extension_require)) { - _mesa_glsl_error(name_locp, state, "Cannot %s all extensions", - (ext_mode == extension_enable) - ? "enable" : "require"); - return false; - } - } else if (strcmp(name, "GL_ARB_draw_buffers") == 0) { - /* This extension is only supported in fragment shaders. - */ - if (state->target != fragment_shader) { - unsupported = true; - } else { - state->ARB_draw_buffers_enable = (ext_mode != extension_disable); - state->ARB_draw_buffers_warn = (ext_mode == extension_warn); - } - } else if (strcmp(name, "GL_ARB_draw_instanced") == 0) { - /* This extension is only supported in vertex shaders. - */ - if (state->target != vertex_shader) { - unsupported = true; - } else { - state->ARB_draw_instanced_enable = (ext_mode != extension_disable); - state->ARB_draw_instanced_warn = (ext_mode == extension_warn); - } - } else if (strcmp(name, "GL_ARB_explicit_attrib_location") == 0) { - state->ARB_explicit_attrib_location_enable = - (ext_mode != extension_disable); - state->ARB_explicit_attrib_location_warn = - (ext_mode == extension_warn); - - unsupported = !state->extensions->ARB_explicit_attrib_location; - } else if (strcmp(name, "GL_ARB_fragment_coord_conventions") == 0) { - state->ARB_fragment_coord_conventions_enable = - (ext_mode != extension_disable); - state->ARB_fragment_coord_conventions_warn = - (ext_mode == extension_warn); - - unsupported = !state->extensions->ARB_fragment_coord_conventions; - } else if (strcmp(name, "GL_ARB_texture_rectangle") == 0) { - state->ARB_texture_rectangle_enable = (ext_mode != extension_disable); - state->ARB_texture_rectangle_warn = (ext_mode == extension_warn); - } else if (strcmp(name, "GL_EXT_texture_array") == 0) { - state->EXT_texture_array_enable = (ext_mode != extension_disable); - state->EXT_texture_array_warn = (ext_mode == extension_warn); - - unsupported = !state->extensions->EXT_texture_array; - } else if (strcmp(name, "GL_ARB_shader_stencil_export") == 0) { - if (state->target != fragment_shader) { - unsupported = true; - } else { - state->ARB_shader_stencil_export_enable = (ext_mode != extension_disable); - state->ARB_shader_stencil_export_warn = (ext_mode == extension_warn); - unsupported = !state->extensions->ARB_shader_stencil_export; - } - } else if (strcmp(name, "GL_AMD_conservative_depth") == 0) { - /* The AMD_conservative spec does not forbid requiring the extension in - * the vertex shader. - */ - state->AMD_conservative_depth_enable = (ext_mode != extension_disable); - state->AMD_conservative_depth_warn = (ext_mode == extension_warn); - unsupported = !state->extensions->AMD_conservative_depth; - } else if (strcmp(name, "GL_OES_texture_3D") == 0 && state->es_shader) { - state->OES_texture_3D_enable = (ext_mode != extension_disable); - state->OES_texture_3D_warn = (ext_mode == extension_warn); - - unsupported = !state->extensions->EXT_texture3D; - } else { - unsupported = true; - } - - if (unsupported) { - static const char *const fmt = "extension `%s' unsupported in %s shader"; - - if (ext_mode == extension_require) { - _mesa_glsl_error(name_locp, state, fmt, - name, _mesa_glsl_shader_target_name(state->target)); - return false; - } else { - _mesa_glsl_warning(name_locp, state, fmt, - name, _mesa_glsl_shader_target_name(state->target)); - } - } - - return true; -} - -void -_mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q) -{ - if (q->flags.q.constant) - printf("const "); - - if (q->flags.q.invariant) - printf("invariant "); - - if (q->flags.q.attribute) - printf("attribute "); - - if (q->flags.q.varying) - printf("varying "); - - if (q->flags.q.in && q->flags.q.out) - printf("inout "); - else { - if (q->flags.q.in) - printf("in "); - - if (q->flags.q.out) - printf("out "); - } - - if (q->flags.q.centroid) - printf("centroid "); - if (q->flags.q.uniform) - printf("uniform "); - if (q->flags.q.smooth) - printf("smooth "); - if (q->flags.q.flat) - printf("flat "); - if (q->flags.q.noperspective) - printf("noperspective "); -} - - -void -ast_node::print(void) const -{ - printf("unhandled node "); -} - - -ast_node::ast_node(void) -{ - this->location.source = 0; - this->location.line = 0; - this->location.column = 0; -} - - -static void -ast_opt_array_size_print(bool is_array, const ast_expression *array_size) -{ - if (is_array) { - printf("[ "); - - if (array_size) - array_size->print(); - - printf("] "); - } -} - - -void -ast_compound_statement::print(void) const -{ - printf("{\n"); - - foreach_list_const(n, &this->statements) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf("}\n"); -} - - -ast_compound_statement::ast_compound_statement(int new_scope, - ast_node *statements) -{ - this->new_scope = new_scope; - - if (statements != NULL) { - this->statements.push_degenerate_list_at_head(&statements->link); - } -} - - -void -ast_expression::print(void) const -{ - switch (oper) { - case ast_assign: - case ast_mul_assign: - case ast_div_assign: - case ast_mod_assign: - case ast_add_assign: - case ast_sub_assign: - case ast_ls_assign: - case ast_rs_assign: - case ast_and_assign: - case ast_xor_assign: - case ast_or_assign: - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - subexpressions[1]->print(); - break; - - case ast_field_selection: - subexpressions[0]->print(); - printf(". %s ", primary_expression.identifier); - break; - - case ast_plus: - case ast_neg: - case ast_bit_not: - case ast_logic_not: - case ast_pre_inc: - case ast_pre_dec: - printf("%s ", operator_string(oper)); - subexpressions[0]->print(); - break; - - case ast_post_inc: - case ast_post_dec: - subexpressions[0]->print(); - printf("%s ", operator_string(oper)); - break; - - case ast_conditional: - subexpressions[0]->print(); - printf("? "); - subexpressions[1]->print(); - printf(": "); - subexpressions[1]->print(); - break; - - case ast_array_index: - subexpressions[0]->print(); - printf("[ "); - subexpressions[1]->print(); - printf("] "); - break; - - case ast_function_call: { - subexpressions[0]->print(); - printf("( "); - - foreach_list_const (n, &this->expressions) { - if (n != this->expressions.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf(") "); - break; - } - - case ast_identifier: - printf("%s ", primary_expression.identifier); - break; - - case ast_int_constant: - printf("%d ", primary_expression.int_constant); - break; - - case ast_uint_constant: - printf("%u ", primary_expression.uint_constant); - break; - - case ast_float_constant: - printf("%f ", primary_expression.float_constant); - break; - - case ast_bool_constant: - printf("%s ", - primary_expression.bool_constant - ? "true" : "false"); - break; - - case ast_sequence: { - printf("( "); - foreach_list_const(n, & this->expressions) { - if (n != this->expressions.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf(") "); - break; - } - - default: - assert(0); - break; - } -} - -ast_expression::ast_expression(int oper, - ast_expression *ex0, - ast_expression *ex1, - ast_expression *ex2) -{ - this->oper = ast_operators(oper); - this->subexpressions[0] = ex0; - this->subexpressions[1] = ex1; - this->subexpressions[2] = ex2; -} - - -void -ast_expression_statement::print(void) const -{ - if (expression) - expression->print(); - - printf("; "); -} - - -ast_expression_statement::ast_expression_statement(ast_expression *ex) : - expression(ex) -{ - /* empty */ -} - - -void -ast_function::print(void) const -{ - return_type->print(); - printf(" %s (", identifier); - - foreach_list_const(n, & this->parameters) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - - printf(")"); -} - - -ast_function::ast_function(void) - : is_definition(false), signature(NULL) -{ - /* empty */ -} - - -void -ast_fully_specified_type::print(void) const -{ - _mesa_ast_type_qualifier_print(& qualifier); - specifier->print(); -} - - -void -ast_parameter_declarator::print(void) const -{ - type->print(); - if (identifier) - printf("%s ", identifier); - ast_opt_array_size_print(is_array, array_size); -} - - -void -ast_function_definition::print(void) const -{ - prototype->print(); - body->print(); -} - - -void -ast_declaration::print(void) const -{ - printf("%s ", identifier); - ast_opt_array_size_print(is_array, array_size); - - if (initializer) { - printf("= "); - initializer->print(); - } -} - - -ast_declaration::ast_declaration(char *identifier, int is_array, - ast_expression *array_size, - ast_expression *initializer) -{ - this->identifier = identifier; - this->is_array = is_array; - this->array_size = array_size; - this->initializer = initializer; -} - - -void -ast_declarator_list::print(void) const -{ - assert(type || invariant); - - if (type) - type->print(); - else - printf("invariant "); - - foreach_list_const (ptr, & this->declarations) { - if (ptr != this->declarations.get_head()) - printf(", "); - - ast_node *ast = exec_node_data(ast_node, ptr, link); - ast->print(); - } - - printf("; "); -} - - -ast_declarator_list::ast_declarator_list(ast_fully_specified_type *type) -{ - this->type = type; - this->invariant = false; -} - -void -ast_jump_statement::print(void) const -{ - switch (mode) { - case ast_continue: - printf("continue; "); - break; - case ast_break: - printf("break; "); - break; - case ast_return: - printf("return "); - if (opt_return_value) - opt_return_value->print(); - - printf("; "); - break; - case ast_discard: - printf("discard; "); - break; - } -} - - -ast_jump_statement::ast_jump_statement(int mode, ast_expression *return_value) -{ - this->mode = ast_jump_modes(mode); - - if (mode == ast_return) - opt_return_value = return_value; -} - - -void -ast_selection_statement::print(void) const -{ - printf("if ( "); - condition->print(); - printf(") "); - - then_statement->print(); - - if (else_statement) { - printf("else "); - else_statement->print(); - } - -} - - -ast_selection_statement::ast_selection_statement(ast_expression *condition, - ast_node *then_statement, - ast_node *else_statement) -{ - this->condition = condition; - this->then_statement = then_statement; - this->else_statement = else_statement; -} - - -void -ast_iteration_statement::print(void) const -{ - switch (mode) { - case ast_for: - printf("for( "); - if (init_statement) - init_statement->print(); - printf("; "); - - if (condition) - condition->print(); - printf("; "); - - if (rest_expression) - rest_expression->print(); - printf(") "); - - body->print(); - break; - - case ast_while: - printf("while ( "); - if (condition) - condition->print(); - printf(") "); - body->print(); - break; - - case ast_do_while: - printf("do "); - body->print(); - printf("while ( "); - if (condition) - condition->print(); - printf("); "); - break; - } -} - - -ast_iteration_statement::ast_iteration_statement(int mode, - ast_node *init, - ast_node *condition, - ast_expression *rest_expression, - ast_node *body) -{ - this->mode = ast_iteration_modes(mode); - this->init_statement = init; - this->condition = condition; - this->rest_expression = rest_expression; - this->body = body; -} - - -void -ast_struct_specifier::print(void) const -{ - printf("struct %s { ", name); - foreach_list_const(n, &this->declarations) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf("} "); -} - - -ast_struct_specifier::ast_struct_specifier(char *identifier, - ast_node *declarator_list) -{ - if (identifier == NULL) { - static unsigned anon_count = 1; - identifier = ralloc_asprintf(this, "#anon_struct_%04x", anon_count); - anon_count++; - } - name = identifier; - this->declarations.push_degenerate_list_at_head(&declarator_list->link); -} - -bool -do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations) -{ - GLboolean progress = GL_FALSE; - - progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress; - - if (linked) { - progress = do_function_inlining(ir) || progress; - progress = do_dead_functions(ir) || progress; - } - progress = do_structure_splitting(ir) || progress; - progress = do_if_simplification(ir) || progress; - progress = do_discard_simplification(ir) || progress; - progress = do_copy_propagation(ir) || progress; - /*progress = do_copy_propagation_elements(ir) || progress;*/ - if (linked) - progress = do_dead_code(ir) || progress; - else - progress = do_dead_code_unlinked(ir) || progress; - progress = do_dead_code_local(ir) || progress; - progress = do_tree_grafting(ir) || progress; - progress = do_constant_propagation(ir) || progress; - if (linked) - progress = do_constant_variable(ir) || progress; - else - progress = do_constant_variable_unlinked(ir) || progress; - progress = do_constant_folding(ir) || progress; - progress = do_algebraic(ir) || progress; - progress = do_lower_jumps(ir) || progress; - progress = do_vec_index_to_swizzle(ir) || progress; - progress = do_swizzle_swizzle(ir) || progress; - progress = do_noop_swizzle(ir) || progress; - - progress = optimize_redundant_jumps(ir) || progress; - - loop_state *ls = analyze_loop_variables(ir); - if (ls->loop_found) { - progress = set_loop_controls(ir, ls) || progress; - progress = unroll_loops(ir, ls, max_unroll_iterations) || progress; - } - delete ls; - - return progress; -} - -extern "C" { - -/** - * To be called at GL teardown time, this frees compiler datastructures. - * - * After calling this, any previously compiled shaders and shader - * programs would be invalid. So this should happen at approximately - * program exit. - */ -void -_mesa_destroy_shader_compiler(void) -{ - _mesa_destroy_shader_compiler_caches(); - - _mesa_glsl_release_types(); -} - -/** - * Releases compiler caches to trade off performance for memory. - * - * Intended to be used with glReleaseShaderCompiler(). - */ -void -_mesa_destroy_shader_compiler_caches(void) -{ - _mesa_glsl_release_functions(); -} - -} +/*
+ * Copyright © 2008, 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+extern "C" {
+#include "main/core.h" /* for struct gl_context */
+}
+
+#include "ralloc.h"
+#include "ast.h"
+#include "glsl_parser_extras.h"
+#include "glsl_parser.h"
+#include "ir_optimization.h"
+#include "loop_analysis.h"
+
+_mesa_glsl_parse_state::_mesa_glsl_parse_state(struct gl_context *ctx,
+ GLenum target, void *mem_ctx)
+{
+ switch (target) {
+ case GL_VERTEX_SHADER: this->target = vertex_shader; break;
+ case GL_FRAGMENT_SHADER: this->target = fragment_shader; break;
+ case GL_GEOMETRY_SHADER: this->target = geometry_shader; break;
+ }
+
+ this->scanner = NULL;
+ this->translation_unit.make_empty();
+ this->symbols = new(mem_ctx) glsl_symbol_table;
+ this->info_log = ralloc_strdup(mem_ctx, "");
+ this->error = false;
+ this->loop_or_switch_nesting = NULL;
+
+ /* Set default language version and extensions */
+ this->language_version = 110;
+ this->es_shader = false;
+ this->ARB_texture_rectangle_enable = true;
+
+ /* OpenGL ES 2.0 has different defaults from desktop GL. */
+ if (ctx->API == API_OPENGLES2) {
+ this->language_version = 100;
+ this->es_shader = true;
+ this->ARB_texture_rectangle_enable = false;
+ }
+
+ this->extensions = &ctx->Extensions;
+
+ this->Const.MaxLights = ctx->Const.MaxLights;
+ this->Const.MaxClipPlanes = ctx->Const.MaxClipPlanes;
+ this->Const.MaxTextureUnits = ctx->Const.MaxTextureUnits;
+ this->Const.MaxTextureCoords = ctx->Const.MaxTextureCoordUnits;
+ this->Const.MaxVertexAttribs = ctx->Const.VertexProgram.MaxAttribs;
+ this->Const.MaxVertexUniformComponents = ctx->Const.VertexProgram.MaxUniformComponents;
+ this->Const.MaxVaryingFloats = ctx->Const.MaxVarying * 4;
+ this->Const.MaxVertexTextureImageUnits = ctx->Const.MaxVertexTextureImageUnits;
+ this->Const.MaxCombinedTextureImageUnits = ctx->Const.MaxCombinedTextureImageUnits;
+ this->Const.MaxTextureImageUnits = ctx->Const.MaxTextureImageUnits;
+ this->Const.MaxFragmentUniformComponents = ctx->Const.FragmentProgram.MaxUniformComponents;
+
+ this->Const.MaxDrawBuffers = ctx->Const.MaxDrawBuffers;
+
+ /* Note: Once the OpenGL 3.0 'forward compatible' context or the OpenGL 3.2
+ * Core context is supported, this logic will need change. Older versions of
+ * GLSL are no longer supported outside the compatibility contexts of 3.x.
+ */
+ this->Const.GLSL_100ES = (ctx->API == API_OPENGLES2)
+ || ctx->Extensions.ARB_ES2_compatibility;
+ this->Const.GLSL_110 = (ctx->API == API_OPENGL);
+ this->Const.GLSL_120 = (ctx->API == API_OPENGL)
+ && (ctx->Const.GLSLVersion >= 120);
+ this->Const.GLSL_130 = (ctx->API == API_OPENGL)
+ && (ctx->Const.GLSLVersion >= 130);
+
+ const unsigned lowest_version =
+ (ctx->API == API_OPENGLES2) || ctx->Extensions.ARB_ES2_compatibility
+ ? 100 : 110;
+ const unsigned highest_version =
+ (ctx->API == API_OPENGL) ? ctx->Const.GLSLVersion : 100;
+ char *supported = ralloc_strdup(this, "");
+
+ for (unsigned ver = lowest_version; ver <= highest_version; ver += 10) {
+ const char *const prefix = (ver == lowest_version)
+ ? ""
+ : ((ver == highest_version) ? ", and " : ", ");
+
+ ralloc_asprintf_append(& supported, "%s%d.%02d%s",
+ prefix,
+ ver / 100, ver % 100,
+ (ver == 100) ? " ES" : "");
+ }
+
+ this->supported_version_string = supported;
+}
+
+const char *
+_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target)
+{
+ switch (target) {
+ case vertex_shader: return "vertex";
+ case fragment_shader: return "fragment";
+ case geometry_shader: return "geometry";
+ }
+
+ assert(!"Should not get here.");
+ return "unknown";
+}
+
+
+void
+_mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ state->error = true;
+
+ assert(state->info_log != NULL);
+ ralloc_asprintf_append(&state->info_log, "%u:%u(%u): error: ",
+ locp->source,
+ locp->first_line,
+ locp->first_column);
+ va_start(ap, fmt);
+ ralloc_vasprintf_append(&state->info_log, fmt, ap);
+ va_end(ap);
+ ralloc_strcat(&state->info_log, "\n");
+}
+
+
+void
+_mesa_glsl_warning(const YYLTYPE *locp, _mesa_glsl_parse_state *state,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ assert(state->info_log != NULL);
+ ralloc_asprintf_append(&state->info_log, "%u:%u(%u): warning: ",
+ locp->source,
+ locp->first_line,
+ locp->first_column);
+ va_start(ap, fmt);
+ ralloc_vasprintf_append(&state->info_log, fmt, ap);
+ va_end(ap);
+ ralloc_strcat(&state->info_log, "\n");
+}
+
+
+bool
+_mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp,
+ const char *behavior, YYLTYPE *behavior_locp,
+ _mesa_glsl_parse_state *state)
+{
+ enum {
+ extension_disable,
+ extension_enable,
+ extension_require,
+ extension_warn
+ } ext_mode;
+
+ if (strcmp(behavior, "warn") == 0) {
+ ext_mode = extension_warn;
+ } else if (strcmp(behavior, "require") == 0) {
+ ext_mode = extension_require;
+ } else if (strcmp(behavior, "enable") == 0) {
+ ext_mode = extension_enable;
+ } else if (strcmp(behavior, "disable") == 0) {
+ ext_mode = extension_disable;
+ } else {
+ _mesa_glsl_error(behavior_locp, state,
+ "Unknown extension behavior `%s'",
+ behavior);
+ return false;
+ }
+
+ bool unsupported = false;
+
+ if (strcmp(name, "all") == 0) {
+ if ((ext_mode == extension_enable) || (ext_mode == extension_require)) {
+ _mesa_glsl_error(name_locp, state, "Cannot %s all extensions",
+ (ext_mode == extension_enable)
+ ? "enable" : "require");
+ return false;
+ }
+ } else if (strcmp(name, "GL_ARB_draw_buffers") == 0) {
+ /* This extension is only supported in fragment shaders.
+ */
+ if (state->target != fragment_shader) {
+ unsupported = true;
+ } else {
+ state->ARB_draw_buffers_enable = (ext_mode != extension_disable);
+ state->ARB_draw_buffers_warn = (ext_mode == extension_warn);
+ }
+ } else if (strcmp(name, "GL_ARB_draw_instanced") == 0) {
+ /* This extension is only supported in vertex shaders.
+ */
+ if (state->target != vertex_shader) {
+ unsupported = true;
+ } else {
+ state->ARB_draw_instanced_enable = (ext_mode != extension_disable);
+ state->ARB_draw_instanced_warn = (ext_mode == extension_warn);
+ }
+ } else if (strcmp(name, "GL_ARB_explicit_attrib_location") == 0) {
+ state->ARB_explicit_attrib_location_enable =
+ (ext_mode != extension_disable);
+ state->ARB_explicit_attrib_location_warn =
+ (ext_mode == extension_warn);
+
+ unsupported = !state->extensions->ARB_explicit_attrib_location;
+ } else if (strcmp(name, "GL_ARB_fragment_coord_conventions") == 0) {
+ state->ARB_fragment_coord_conventions_enable =
+ (ext_mode != extension_disable);
+ state->ARB_fragment_coord_conventions_warn =
+ (ext_mode == extension_warn);
+
+ unsupported = !state->extensions->ARB_fragment_coord_conventions;
+ } else if (strcmp(name, "GL_ARB_texture_rectangle") == 0) {
+ state->ARB_texture_rectangle_enable = (ext_mode != extension_disable);
+ state->ARB_texture_rectangle_warn = (ext_mode == extension_warn);
+ } else if (strcmp(name, "GL_EXT_texture_array") == 0) {
+ state->EXT_texture_array_enable = (ext_mode != extension_disable);
+ state->EXT_texture_array_warn = (ext_mode == extension_warn);
+
+ unsupported = !state->extensions->EXT_texture_array;
+ } else if (strcmp(name, "GL_ARB_shader_stencil_export") == 0) {
+ if (state->target != fragment_shader) {
+ unsupported = true;
+ } else {
+ state->ARB_shader_stencil_export_enable = (ext_mode != extension_disable);
+ state->ARB_shader_stencil_export_warn = (ext_mode == extension_warn);
+ unsupported = !state->extensions->ARB_shader_stencil_export;
+ }
+ } else if (strcmp(name, "GL_AMD_conservative_depth") == 0) {
+ /* The AMD_conservative spec does not forbid requiring the extension in
+ * the vertex shader.
+ */
+ state->AMD_conservative_depth_enable = (ext_mode != extension_disable);
+ state->AMD_conservative_depth_warn = (ext_mode == extension_warn);
+ unsupported = !state->extensions->AMD_conservative_depth;
+ } else if (strcmp(name, "GL_OES_texture_3D") == 0 && state->es_shader) {
+ state->OES_texture_3D_enable = (ext_mode != extension_disable);
+ state->OES_texture_3D_warn = (ext_mode == extension_warn);
+
+ unsupported = !state->extensions->EXT_texture3D;
+ } else {
+ unsupported = true;
+ }
+
+ if (unsupported) {
+ static const char *const fmt = "extension `%s' unsupported in %s shader";
+
+ if (ext_mode == extension_require) {
+ _mesa_glsl_error(name_locp, state, fmt,
+ name, _mesa_glsl_shader_target_name(state->target));
+ return false;
+ } else {
+ _mesa_glsl_warning(name_locp, state, fmt,
+ name, _mesa_glsl_shader_target_name(state->target));
+ }
+ }
+
+ return true;
+}
+
+void
+_mesa_ast_type_qualifier_print(const struct ast_type_qualifier *q)
+{
+ if (q->flags.q.constant)
+ printf("const ");
+
+ if (q->flags.q.invariant)
+ printf("invariant ");
+
+ if (q->flags.q.attribute)
+ printf("attribute ");
+
+ if (q->flags.q.varying)
+ printf("varying ");
+
+ if (q->flags.q.in && q->flags.q.out)
+ printf("inout ");
+ else {
+ if (q->flags.q.in)
+ printf("in ");
+
+ if (q->flags.q.out)
+ printf("out ");
+ }
+
+ if (q->flags.q.centroid)
+ printf("centroid ");
+ if (q->flags.q.uniform)
+ printf("uniform ");
+ if (q->flags.q.smooth)
+ printf("smooth ");
+ if (q->flags.q.flat)
+ printf("flat ");
+ if (q->flags.q.noperspective)
+ printf("noperspective ");
+}
+
+
+void
+ast_node::print(void) const
+{
+ printf("unhandled node ");
+}
+
+
+ast_node::ast_node(void)
+{
+ this->location.source = 0;
+ this->location.line = 0;
+ this->location.column = 0;
+}
+
+
+static void
+ast_opt_array_size_print(bool is_array, const ast_expression *array_size)
+{
+ if (is_array) {
+ printf("[ ");
+
+ if (array_size)
+ array_size->print();
+
+ printf("] ");
+ }
+}
+
+
+void
+ast_compound_statement::print(void) const
+{
+ printf("{\n");
+
+ foreach_list_const(n, &this->statements) {
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+
+ printf("}\n");
+}
+
+
+ast_compound_statement::ast_compound_statement(int new_scope,
+ ast_node *statements)
+{
+ this->new_scope = new_scope;
+
+ if (statements != NULL) {
+ this->statements.push_degenerate_list_at_head(&statements->link);
+ }
+}
+
+
+void
+ast_expression::print(void) const
+{
+ switch (oper) {
+ case ast_assign:
+ case ast_mul_assign:
+ case ast_div_assign:
+ case ast_mod_assign:
+ case ast_add_assign:
+ case ast_sub_assign:
+ case ast_ls_assign:
+ case ast_rs_assign:
+ case ast_and_assign:
+ case ast_xor_assign:
+ case ast_or_assign:
+ subexpressions[0]->print();
+ printf("%s ", operator_string(oper));
+ subexpressions[1]->print();
+ break;
+
+ case ast_field_selection:
+ subexpressions[0]->print();
+ printf(". %s ", primary_expression.identifier);
+ break;
+
+ case ast_plus:
+ case ast_neg:
+ case ast_bit_not:
+ case ast_logic_not:
+ case ast_pre_inc:
+ case ast_pre_dec:
+ printf("%s ", operator_string(oper));
+ subexpressions[0]->print();
+ break;
+
+ case ast_post_inc:
+ case ast_post_dec:
+ subexpressions[0]->print();
+ printf("%s ", operator_string(oper));
+ break;
+
+ case ast_conditional:
+ subexpressions[0]->print();
+ printf("? ");
+ subexpressions[1]->print();
+ printf(": ");
+ subexpressions[1]->print();
+ break;
+
+ case ast_array_index:
+ subexpressions[0]->print();
+ printf("[ ");
+ subexpressions[1]->print();
+ printf("] ");
+ break;
+
+ case ast_function_call: {
+ subexpressions[0]->print();
+ printf("( ");
+
+ foreach_list_const (n, &this->expressions) {
+ if (n != this->expressions.get_head())
+ printf(", ");
+
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+
+ printf(") ");
+ break;
+ }
+
+ case ast_identifier:
+ printf("%s ", primary_expression.identifier);
+ break;
+
+ case ast_int_constant:
+ printf("%d ", primary_expression.int_constant);
+ break;
+
+ case ast_uint_constant:
+ printf("%u ", primary_expression.uint_constant);
+ break;
+
+ case ast_float_constant:
+ printf("%f ", primary_expression.float_constant);
+ break;
+
+ case ast_bool_constant:
+ printf("%s ",
+ primary_expression.bool_constant
+ ? "true" : "false");
+ break;
+
+ case ast_sequence: {
+ printf("( ");
+ foreach_list_const(n, & this->expressions) {
+ if (n != this->expressions.get_head())
+ printf(", ");
+
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+ printf(") ");
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+}
+
+ast_expression::ast_expression(int oper,
+ ast_expression *ex0,
+ ast_expression *ex1,
+ ast_expression *ex2)
+{
+ this->oper = ast_operators(oper);
+ this->subexpressions[0] = ex0;
+ this->subexpressions[1] = ex1;
+ this->subexpressions[2] = ex2;
+}
+
+
+void
+ast_expression_statement::print(void) const
+{
+ if (expression)
+ expression->print();
+
+ printf("; ");
+}
+
+
+ast_expression_statement::ast_expression_statement(ast_expression *ex) :
+ expression(ex)
+{
+ /* empty */
+}
+
+
+void
+ast_function::print(void) const
+{
+ return_type->print();
+ printf(" %s (", identifier);
+
+ foreach_list_const(n, & this->parameters) {
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+
+ printf(")");
+}
+
+
+ast_function::ast_function(void)
+ : is_definition(false), signature(NULL)
+{
+ /* empty */
+}
+
+
+void
+ast_fully_specified_type::print(void) const
+{
+ _mesa_ast_type_qualifier_print(& qualifier);
+ specifier->print();
+}
+
+
+void
+ast_parameter_declarator::print(void) const
+{
+ type->print();
+ if (identifier)
+ printf("%s ", identifier);
+ ast_opt_array_size_print(is_array, array_size);
+}
+
+
+void
+ast_function_definition::print(void) const
+{
+ prototype->print();
+ body->print();
+}
+
+
+void
+ast_declaration::print(void) const
+{
+ printf("%s ", identifier);
+ ast_opt_array_size_print(is_array, array_size);
+
+ if (initializer) {
+ printf("= ");
+ initializer->print();
+ }
+}
+
+
+ast_declaration::ast_declaration(char *identifier, int is_array,
+ ast_expression *array_size,
+ ast_expression *initializer)
+{
+ this->identifier = identifier;
+ this->is_array = is_array;
+ this->array_size = array_size;
+ this->initializer = initializer;
+}
+
+
+void
+ast_declarator_list::print(void) const
+{
+ assert(type || invariant);
+
+ if (type)
+ type->print();
+ else
+ printf("invariant ");
+
+ foreach_list_const (ptr, & this->declarations) {
+ if (ptr != this->declarations.get_head())
+ printf(", ");
+
+ ast_node *ast = exec_node_data(ast_node, ptr, link);
+ ast->print();
+ }
+
+ printf("; ");
+}
+
+
+ast_declarator_list::ast_declarator_list(ast_fully_specified_type *type)
+{
+ this->type = type;
+ this->invariant = false;
+}
+
+void
+ast_jump_statement::print(void) const
+{
+ switch (mode) {
+ case ast_continue:
+ printf("continue; ");
+ break;
+ case ast_break:
+ printf("break; ");
+ break;
+ case ast_return:
+ printf("return ");
+ if (opt_return_value)
+ opt_return_value->print();
+
+ printf("; ");
+ break;
+ case ast_discard:
+ printf("discard; ");
+ break;
+ }
+}
+
+
+ast_jump_statement::ast_jump_statement(int mode, ast_expression *return_value)
+{
+ this->mode = ast_jump_modes(mode);
+
+ if (mode == ast_return)
+ opt_return_value = return_value;
+}
+
+
+void
+ast_selection_statement::print(void) const
+{
+ printf("if ( ");
+ condition->print();
+ printf(") ");
+
+ then_statement->print();
+
+ if (else_statement) {
+ printf("else ");
+ else_statement->print();
+ }
+
+}
+
+
+ast_selection_statement::ast_selection_statement(ast_expression *condition,
+ ast_node *then_statement,
+ ast_node *else_statement)
+{
+ this->condition = condition;
+ this->then_statement = then_statement;
+ this->else_statement = else_statement;
+}
+
+
+void
+ast_iteration_statement::print(void) const
+{
+ switch (mode) {
+ case ast_for:
+ printf("for( ");
+ if (init_statement)
+ init_statement->print();
+ printf("; ");
+
+ if (condition)
+ condition->print();
+ printf("; ");
+
+ if (rest_expression)
+ rest_expression->print();
+ printf(") ");
+
+ body->print();
+ break;
+
+ case ast_while:
+ printf("while ( ");
+ if (condition)
+ condition->print();
+ printf(") ");
+ body->print();
+ break;
+
+ case ast_do_while:
+ printf("do ");
+ body->print();
+ printf("while ( ");
+ if (condition)
+ condition->print();
+ printf("); ");
+ break;
+ }
+}
+
+
+ast_iteration_statement::ast_iteration_statement(int mode,
+ ast_node *init,
+ ast_node *condition,
+ ast_expression *rest_expression,
+ ast_node *body)
+{
+ this->mode = ast_iteration_modes(mode);
+ this->init_statement = init;
+ this->condition = condition;
+ this->rest_expression = rest_expression;
+ this->body = body;
+}
+
+
+void
+ast_struct_specifier::print(void) const
+{
+ printf("struct %s { ", name);
+ foreach_list_const(n, &this->declarations) {
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+ printf("} ");
+}
+
+
+ast_struct_specifier::ast_struct_specifier(char *identifier,
+ ast_node *declarator_list)
+{
+ if (identifier == NULL) {
+ static unsigned anon_count = 1;
+ identifier = ralloc_asprintf(this, "#anon_struct_%04x", anon_count);
+ anon_count++;
+ }
+ name = identifier;
+ this->declarations.push_degenerate_list_at_head(&declarator_list->link);
+}
+
+bool
+do_common_optimization(exec_list *ir, bool linked, unsigned max_unroll_iterations)
+{
+ GLboolean progress = GL_FALSE;
+
+ progress = lower_instructions(ir, SUB_TO_ADD_NEG) || progress;
+
+ if (linked) {
+ progress = do_function_inlining(ir) || progress;
+ progress = do_dead_functions(ir) || progress;
+ }
+ progress = do_structure_splitting(ir) || progress;
+ progress = do_if_simplification(ir) || progress;
+ progress = do_discard_simplification(ir) || progress;
+ progress = do_copy_propagation(ir) || progress;
+ /*progress = do_copy_propagation_elements(ir) || progress;*/
+ if (linked)
+ progress = do_dead_code(ir) || progress;
+ else
+ progress = do_dead_code_unlinked(ir) || progress;
+ progress = do_dead_code_local(ir) || progress;
+ progress = do_tree_grafting(ir) || progress;
+ progress = do_constant_propagation(ir) || progress;
+ if (linked)
+ progress = do_constant_variable(ir) || progress;
+ else
+ progress = do_constant_variable_unlinked(ir) || progress;
+ progress = do_constant_folding(ir) || progress;
+ progress = do_algebraic(ir) || progress;
+ progress = do_lower_jumps(ir) || progress;
+ progress = do_vec_index_to_swizzle(ir) || progress;
+ progress = do_swizzle_swizzle(ir) || progress;
+ progress = do_noop_swizzle(ir) || progress;
+
+ progress = optimize_redundant_jumps(ir) || progress;
+
+ loop_state *ls = analyze_loop_variables(ir);
+ if (ls->loop_found) {
+ progress = set_loop_controls(ir, ls) || progress;
+ progress = unroll_loops(ir, ls, max_unroll_iterations) || progress;
+ }
+ delete ls;
+
+ return progress;
+}
+
+extern "C" {
+
+/**
+ * To be called at GL teardown time, this frees compiler datastructures.
+ *
+ * After calling this, any previously compiled shaders and shader
+ * programs would be invalid. So this should happen at approximately
+ * program exit.
+ */
+void
+_mesa_destroy_shader_compiler(void)
+{
+ _mesa_destroy_shader_compiler_caches();
+
+ _mesa_glsl_release_types();
+}
+
+/**
+ * Releases compiler caches to trade off performance for memory.
+ *
+ * Intended to be used with glReleaseShaderCompiler().
+ */
+void
+_mesa_destroy_shader_compiler_caches(void)
+{
+ _mesa_glsl_release_functions();
+}
+
+}
diff --git a/mesalib/src/glsl/glsl_parser_extras.h b/mesalib/src/glsl/glsl_parser_extras.h index b5c016fb3..6351413f1 100644 --- a/mesalib/src/glsl/glsl_parser_extras.h +++ b/mesalib/src/glsl/glsl_parser_extras.h @@ -1,279 +1,283 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#pragma once -#ifndef GLSL_PARSER_EXTRAS_H -#define GLSL_PARSER_EXTRAS_H - -/* - * Most of the definitions here only apply to C++ - */ -#ifdef __cplusplus - - -#include <stdlib.h> -#include "glsl_symbol_table.h" - -enum _mesa_glsl_parser_targets { - vertex_shader, - geometry_shader, - fragment_shader -}; - -struct gl_context; - -struct _mesa_glsl_parse_state { - _mesa_glsl_parse_state(struct gl_context *ctx, GLenum target, - void *mem_ctx); - - /* Callers of this ralloc-based new need not call delete. It's - * easier to just ralloc_free 'ctx' (or any of its ancestors). */ - static void* operator new(size_t size, void *ctx) - { - void *mem = rzalloc_size(ctx, size); - assert(mem != NULL); - - return mem; - } - - /* If the user *does* call delete, that's OK, we will just - * ralloc_free in that case. */ - static void operator delete(void *mem) - { - ralloc_free(mem); - } - - void *scanner; - exec_list translation_unit; - glsl_symbol_table *symbols; - - bool es_shader; - unsigned language_version; - const char *version_string; - enum _mesa_glsl_parser_targets target; - - /** - * Printable list of GLSL versions supported by the current context - * - * \note - * This string should probably be generated per-context instead of per - * invokation of the compiler. This should be changed when the method of - * tracking supported GLSL versions changes. - */ - const char *supported_version_string; - - /** - * Implementation defined limits that affect built-in variables, etc. - * - * \sa struct gl_constants (in mtypes.h) - */ - struct { - /* 1.10 */ - unsigned MaxLights; - unsigned MaxClipPlanes; - unsigned MaxTextureUnits; - unsigned MaxTextureCoords; - unsigned MaxVertexAttribs; - unsigned MaxVertexUniformComponents; - unsigned MaxVaryingFloats; - unsigned MaxVertexTextureImageUnits; - unsigned MaxCombinedTextureImageUnits; - unsigned MaxTextureImageUnits; - unsigned MaxFragmentUniformComponents; - - /* ARB_draw_buffers */ - unsigned MaxDrawBuffers; - - /** - * Set of GLSL versions supported by the current context - * - * Knowing that version X is supported doesn't mean that versions before - * X are also supported. Version 1.00 is only supported in an ES2 - * context or when GL_ARB_ES2_compatibility is supported. In an OpenGL - * 3.0 "forward compatible" context, GLSL 1.10 and 1.20 are \b not - * supported. - */ - /*@{*/ - unsigned GLSL_100ES:1; - unsigned GLSL_110:1; - unsigned GLSL_120:1; - unsigned GLSL_130:1; - /*@}*/ - } Const; - - /** - * During AST to IR conversion, pointer to current IR function - * - * Will be \c NULL whenever the AST to IR conversion is not inside a - * function definition. - */ - class ir_function_signature *current_function; - - /** Have we found a return statement in this function? */ - bool found_return; - - /** Was there an error during compilation? */ - bool error; - - /** - * Are all shader inputs / outputs invariant? - * - * This is set when the 'STDGL invariant(all)' pragma is used. - */ - bool all_invariant; - - /** Loop or switch statement containing the current instructions. */ - class ir_instruction *loop_or_switch_nesting; - class ast_iteration_statement *loop_or_switch_nesting_ast; - - /** List of structures defined in user code. */ - const glsl_type **user_structures; - unsigned num_user_structures; - - char *info_log; - - /** - * \name Enable bits for GLSL extensions - */ - /*@{*/ - unsigned ARB_draw_buffers_enable:1; - unsigned ARB_draw_buffers_warn:1; - unsigned ARB_draw_instanced_enable:1; - unsigned ARB_draw_instanced_warn:1; - unsigned ARB_explicit_attrib_location_enable:1; - unsigned ARB_explicit_attrib_location_warn:1; - unsigned ARB_fragment_coord_conventions_enable:1; - unsigned ARB_fragment_coord_conventions_warn:1; - unsigned ARB_texture_rectangle_enable:1; - unsigned ARB_texture_rectangle_warn:1; - unsigned EXT_texture_array_enable:1; - unsigned EXT_texture_array_warn:1; - unsigned ARB_shader_stencil_export_enable:1; - unsigned ARB_shader_stencil_export_warn:1; - unsigned AMD_conservative_depth_enable:1; - unsigned AMD_conservative_depth_warn:1; - unsigned OES_texture_3D_enable:1; - unsigned OES_texture_3D_warn:1; - /*@}*/ - - /** Extensions supported by the OpenGL implementation. */ - const struct gl_extensions *extensions; - - /** Shaders containing built-in functions that are used for linking. */ - struct gl_shader *builtins_to_link[16]; - unsigned num_builtins_to_link; -}; - -typedef struct YYLTYPE { - int first_line; - int first_column; - int last_line; - int last_column; - unsigned source; -} YYLTYPE; -# define YYLTYPE_IS_DECLARED 1 -# define YYLTYPE_IS_TRIVIAL 1 - -# define YYLLOC_DEFAULT(Current, Rhs, N) \ -do { \ - if (N) \ - { \ - (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ - (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \ - (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ - (Current).last_column = YYRHSLOC(Rhs, N).last_column; \ - } \ - else \ - { \ - (Current).first_line = (Current).last_line = \ - YYRHSLOC(Rhs, 0).last_line; \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC(Rhs, 0).last_column; \ - } \ - (Current).source = 0; \ -} while (0) - -extern void _mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state, - const char *fmt, ...); - -/** - * Emit a warning to the shader log - * - * \sa _mesa_glsl_error - */ -extern void _mesa_glsl_warning(const YYLTYPE *locp, - _mesa_glsl_parse_state *state, - const char *fmt, ...); - -extern void _mesa_glsl_lexer_ctor(struct _mesa_glsl_parse_state *state, - const char *string); - -extern void _mesa_glsl_lexer_dtor(struct _mesa_glsl_parse_state *state); - -union YYSTYPE; -extern int _mesa_glsl_lex(union YYSTYPE *yylval, YYLTYPE *yylloc, - void *scanner); - -extern int _mesa_glsl_parse(struct _mesa_glsl_parse_state *); - -/** - * Process elements of the #extension directive - * - * \return - * If \c name and \c behavior are valid, \c true is returned. Otherwise - * \c false is returned. - */ -extern bool _mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp, - const char *behavior, - YYLTYPE *behavior_locp, - _mesa_glsl_parse_state *state); - -/** - * Get the textual name of the specified shader target - */ -extern const char * -_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target); - - -#endif /* __cplusplus */ - - -/* - * These definitions apply to C and C++ - */ -#ifdef __cplusplus -extern "C" { -#endif - -extern int preprocess(void *ctx, const char **shader, char **info_log, - const struct gl_extensions *extensions, int api); - -extern void _mesa_destroy_shader_compiler(); -extern void _mesa_destroy_shader_compiler_caches(); - -#ifdef __cplusplus -} -#endif - - -#endif /* GLSL_PARSER_EXTRAS_H */ +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+#ifndef GLSL_PARSER_EXTRAS_H
+#define GLSL_PARSER_EXTRAS_H
+
+/*
+ * Most of the definitions here only apply to C++
+ */
+#ifdef __cplusplus
+
+
+#include <stdlib.h>
+#include "glsl_symbol_table.h"
+
+enum _mesa_glsl_parser_targets {
+ vertex_shader,
+ geometry_shader,
+ fragment_shader
+};
+
+struct gl_context;
+
+struct _mesa_glsl_parse_state {
+ _mesa_glsl_parse_state(struct gl_context *ctx, GLenum target,
+ void *mem_ctx);
+
+ /* Callers of this ralloc-based new need not call delete. It's
+ * easier to just ralloc_free 'ctx' (or any of its ancestors). */
+ static void* operator new(size_t size, void *ctx)
+ {
+ void *mem = rzalloc_size(ctx, size);
+ assert(mem != NULL);
+
+ return mem;
+ }
+
+ /* If the user *does* call delete, that's OK, we will just
+ * ralloc_free in that case. */
+ static void operator delete(void *mem, void *ctx)
+ {
+ ralloc_free(mem);
+ }
+ static void operator delete(void *mem)
+ {
+ ralloc_free(mem);
+ }
+
+ void *scanner;
+ exec_list translation_unit;
+ glsl_symbol_table *symbols;
+
+ bool es_shader;
+ unsigned language_version;
+ const char *version_string;
+ enum _mesa_glsl_parser_targets target;
+
+ /**
+ * Printable list of GLSL versions supported by the current context
+ *
+ * \note
+ * This string should probably be generated per-context instead of per
+ * invokation of the compiler. This should be changed when the method of
+ * tracking supported GLSL versions changes.
+ */
+ const char *supported_version_string;
+
+ /**
+ * Implementation defined limits that affect built-in variables, etc.
+ *
+ * \sa struct gl_constants (in mtypes.h)
+ */
+ struct {
+ /* 1.10 */
+ unsigned MaxLights;
+ unsigned MaxClipPlanes;
+ unsigned MaxTextureUnits;
+ unsigned MaxTextureCoords;
+ unsigned MaxVertexAttribs;
+ unsigned MaxVertexUniformComponents;
+ unsigned MaxVaryingFloats;
+ unsigned MaxVertexTextureImageUnits;
+ unsigned MaxCombinedTextureImageUnits;
+ unsigned MaxTextureImageUnits;
+ unsigned MaxFragmentUniformComponents;
+
+ /* ARB_draw_buffers */
+ unsigned MaxDrawBuffers;
+
+ /**
+ * Set of GLSL versions supported by the current context
+ *
+ * Knowing that version X is supported doesn't mean that versions before
+ * X are also supported. Version 1.00 is only supported in an ES2
+ * context or when GL_ARB_ES2_compatibility is supported. In an OpenGL
+ * 3.0 "forward compatible" context, GLSL 1.10 and 1.20 are \b not
+ * supported.
+ */
+ /*@{*/
+ unsigned GLSL_100ES:1;
+ unsigned GLSL_110:1;
+ unsigned GLSL_120:1;
+ unsigned GLSL_130:1;
+ /*@}*/
+ } Const;
+
+ /**
+ * During AST to IR conversion, pointer to current IR function
+ *
+ * Will be \c NULL whenever the AST to IR conversion is not inside a
+ * function definition.
+ */
+ class ir_function_signature *current_function;
+
+ /** Have we found a return statement in this function? */
+ bool found_return;
+
+ /** Was there an error during compilation? */
+ bool error;
+
+ /**
+ * Are all shader inputs / outputs invariant?
+ *
+ * This is set when the 'STDGL invariant(all)' pragma is used.
+ */
+ bool all_invariant;
+
+ /** Loop or switch statement containing the current instructions. */
+ class ir_instruction *loop_or_switch_nesting;
+ class ast_iteration_statement *loop_or_switch_nesting_ast;
+
+ /** List of structures defined in user code. */
+ const glsl_type **user_structures;
+ unsigned num_user_structures;
+
+ char *info_log;
+
+ /**
+ * \name Enable bits for GLSL extensions
+ */
+ /*@{*/
+ unsigned ARB_draw_buffers_enable:1;
+ unsigned ARB_draw_buffers_warn:1;
+ unsigned ARB_draw_instanced_enable:1;
+ unsigned ARB_draw_instanced_warn:1;
+ unsigned ARB_explicit_attrib_location_enable:1;
+ unsigned ARB_explicit_attrib_location_warn:1;
+ unsigned ARB_fragment_coord_conventions_enable:1;
+ unsigned ARB_fragment_coord_conventions_warn:1;
+ unsigned ARB_texture_rectangle_enable:1;
+ unsigned ARB_texture_rectangle_warn:1;
+ unsigned EXT_texture_array_enable:1;
+ unsigned EXT_texture_array_warn:1;
+ unsigned ARB_shader_stencil_export_enable:1;
+ unsigned ARB_shader_stencil_export_warn:1;
+ unsigned AMD_conservative_depth_enable:1;
+ unsigned AMD_conservative_depth_warn:1;
+ unsigned OES_texture_3D_enable:1;
+ unsigned OES_texture_3D_warn:1;
+ /*@}*/
+
+ /** Extensions supported by the OpenGL implementation. */
+ const struct gl_extensions *extensions;
+
+ /** Shaders containing built-in functions that are used for linking. */
+ struct gl_shader *builtins_to_link[16];
+ unsigned num_builtins_to_link;
+};
+
+typedef struct YYLTYPE {
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ unsigned source;
+} YYLTYPE;
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+do { \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC(Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC(Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC(Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC(Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC(Rhs, 0).last_column; \
+ } \
+ (Current).source = 0; \
+} while (0)
+
+extern void _mesa_glsl_error(YYLTYPE *locp, _mesa_glsl_parse_state *state,
+ const char *fmt, ...);
+
+/**
+ * Emit a warning to the shader log
+ *
+ * \sa _mesa_glsl_error
+ */
+extern void _mesa_glsl_warning(const YYLTYPE *locp,
+ _mesa_glsl_parse_state *state,
+ const char *fmt, ...);
+
+extern void _mesa_glsl_lexer_ctor(struct _mesa_glsl_parse_state *state,
+ const char *string);
+
+extern void _mesa_glsl_lexer_dtor(struct _mesa_glsl_parse_state *state);
+
+union YYSTYPE;
+extern int _mesa_glsl_lex(union YYSTYPE *yylval, YYLTYPE *yylloc,
+ void *scanner);
+
+extern int _mesa_glsl_parse(struct _mesa_glsl_parse_state *);
+
+/**
+ * Process elements of the #extension directive
+ *
+ * \return
+ * If \c name and \c behavior are valid, \c true is returned. Otherwise
+ * \c false is returned.
+ */
+extern bool _mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp,
+ const char *behavior,
+ YYLTYPE *behavior_locp,
+ _mesa_glsl_parse_state *state);
+
+/**
+ * Get the textual name of the specified shader target
+ */
+extern const char *
+_mesa_glsl_shader_target_name(enum _mesa_glsl_parser_targets target);
+
+
+#endif /* __cplusplus */
+
+
+/*
+ * These definitions apply to C and C++
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int preprocess(void *ctx, const char **shader, char **info_log,
+ const struct gl_extensions *extensions, int api);
+
+extern void _mesa_destroy_shader_compiler();
+extern void _mesa_destroy_shader_compiler_caches();
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* GLSL_PARSER_EXTRAS_H */
diff --git a/mesalib/src/glsl/glsl_symbol_table.cpp b/mesalib/src/glsl/glsl_symbol_table.cpp index bcb65d301..fa6458df5 100644 --- a/mesalib/src/glsl/glsl_symbol_table.cpp +++ b/mesalib/src/glsl/glsl_symbol_table.cpp @@ -1,165 +1,169 @@ -/* -*- c++ -*- */ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "glsl_symbol_table.h" - -class symbol_table_entry { -public: - /* Callers of this ralloc-based new need not call delete. It's - * easier to just ralloc_free 'ctx' (or any of its ancestors). */ - static void* operator new(size_t size, void *ctx) - { - void *entry = ralloc_size(ctx, size); - assert(entry != NULL); - return entry; - } - - /* If the user *does* call delete, that's OK, we will just ralloc_free. */ - static void operator delete(void *entry) - { - ralloc_free(entry); - } - - symbol_table_entry(ir_variable *v) : v(v), f(0), t(0) {} - symbol_table_entry(ir_function *f) : v(0), f(f), t(0) {} - symbol_table_entry(const glsl_type *t) : v(0), f(0), t(t) {} - - ir_variable *v; - ir_function *f; - const glsl_type *t; -}; - -glsl_symbol_table::glsl_symbol_table() -{ - this->language_version = 120; - this->table = _mesa_symbol_table_ctor(); - this->mem_ctx = ralloc_context(NULL); -} - -glsl_symbol_table::~glsl_symbol_table() -{ - _mesa_symbol_table_dtor(table); - ralloc_free(mem_ctx); -} - -void glsl_symbol_table::push_scope() -{ - _mesa_symbol_table_push_scope(table); -} - -void glsl_symbol_table::pop_scope() -{ - _mesa_symbol_table_pop_scope(table); -} - -bool glsl_symbol_table::name_declared_this_scope(const char *name) -{ - return _mesa_symbol_table_symbol_scope(table, -1, name) == 0; -} - -bool glsl_symbol_table::add_variable(ir_variable *v) -{ - if (this->language_version == 110) { - /* In 1.10, functions and variables have separate namespaces. */ - symbol_table_entry *existing = get_entry(v->name); - if (name_declared_this_scope(v->name)) { - /* If there's already an existing function (not a constructor!) in - * the current scope, just update the existing entry to include 'v'. - */ - if (existing->v == NULL && existing->t == NULL) { - existing->v = v; - return true; - } - } else { - /* If not declared at this scope, add a new entry. But if an existing - * entry includes a function, propagate that to this block - otherwise - * the new variable declaration would shadow the function. - */ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); - if (existing != NULL) - entry->f = existing->f; - int added = _mesa_symbol_table_add_symbol(table, -1, v->name, entry); - assert(added == 0); - (void)added; - return true; - } - return false; - } - - /* 1.20+ rules: */ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v); - return _mesa_symbol_table_add_symbol(table, -1, v->name, entry) == 0; -} - -bool glsl_symbol_table::add_type(const char *name, const glsl_type *t) -{ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(t); - return _mesa_symbol_table_add_symbol(table, -1, name, entry) == 0; -} - -bool glsl_symbol_table::add_function(ir_function *f) -{ - if (this->language_version == 110 && name_declared_this_scope(f->name)) { - /* In 1.10, functions and variables have separate namespaces. */ - symbol_table_entry *existing = get_entry(f->name); - if ((existing->f == NULL) && (existing->t == NULL)) { - existing->f = f; - return true; - } - } - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); - return _mesa_symbol_table_add_symbol(table, -1, f->name, entry) == 0; -} - -void glsl_symbol_table::add_global_function(ir_function *f) -{ - symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f); - int added = _mesa_symbol_table_add_global_symbol(table, -1, f->name, entry); - assert(added == 0); - (void)added; -} - -ir_variable *glsl_symbol_table::get_variable(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->v : NULL; -} - -const glsl_type *glsl_symbol_table::get_type(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->t : NULL; -} - -ir_function *glsl_symbol_table::get_function(const char *name) -{ - symbol_table_entry *entry = get_entry(name); - return entry != NULL ? entry->f : NULL; -} - -symbol_table_entry *glsl_symbol_table::get_entry(const char *name) -{ - return (symbol_table_entry *) - _mesa_symbol_table_find_symbol(table, -1, name); -} +/* -*- c++ -*- */
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "glsl_symbol_table.h"
+
+class symbol_table_entry {
+public:
+ /* Callers of this ralloc-based new need not call delete. It's
+ * easier to just ralloc_free 'ctx' (or any of its ancestors). */
+ static void* operator new(size_t size, void *ctx)
+ {
+ void *entry = ralloc_size(ctx, size);
+ assert(entry != NULL);
+ return entry;
+ }
+
+ /* If the user *does* call delete, that's OK, we will just ralloc_free. */
+ static void operator delete(void *entry, void *ctx)
+ {
+ ralloc_free(entry);
+ }
+ static void operator delete(void *entry)
+ {
+ ralloc_free(entry);
+ }
+
+ symbol_table_entry(ir_variable *v) : v(v), f(0), t(0) {}
+ symbol_table_entry(ir_function *f) : v(0), f(f), t(0) {}
+ symbol_table_entry(const glsl_type *t) : v(0), f(0), t(t) {}
+
+ ir_variable *v;
+ ir_function *f;
+ const glsl_type *t;
+};
+
+glsl_symbol_table::glsl_symbol_table()
+{
+ this->language_version = 120;
+ this->table = _mesa_symbol_table_ctor();
+ this->mem_ctx = ralloc_context(NULL);
+}
+
+glsl_symbol_table::~glsl_symbol_table()
+{
+ _mesa_symbol_table_dtor(table);
+ ralloc_free(mem_ctx);
+}
+
+void glsl_symbol_table::push_scope()
+{
+ _mesa_symbol_table_push_scope(table);
+}
+
+void glsl_symbol_table::pop_scope()
+{
+ _mesa_symbol_table_pop_scope(table);
+}
+
+bool glsl_symbol_table::name_declared_this_scope(const char *name)
+{
+ return _mesa_symbol_table_symbol_scope(table, -1, name) == 0;
+}
+
+bool glsl_symbol_table::add_variable(ir_variable *v)
+{
+ if (this->language_version == 110) {
+ /* In 1.10, functions and variables have separate namespaces. */
+ symbol_table_entry *existing = get_entry(v->name);
+ if (name_declared_this_scope(v->name)) {
+ /* If there's already an existing function (not a constructor!) in
+ * the current scope, just update the existing entry to include 'v'.
+ */
+ if (existing->v == NULL && existing->t == NULL) {
+ existing->v = v;
+ return true;
+ }
+ } else {
+ /* If not declared at this scope, add a new entry. But if an existing
+ * entry includes a function, propagate that to this block - otherwise
+ * the new variable declaration would shadow the function.
+ */
+ symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v);
+ if (existing != NULL)
+ entry->f = existing->f;
+ int added = _mesa_symbol_table_add_symbol(table, -1, v->name, entry);
+ assert(added == 0);
+ (void)added;
+ return true;
+ }
+ return false;
+ }
+
+ /* 1.20+ rules: */
+ symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(v);
+ return _mesa_symbol_table_add_symbol(table, -1, v->name, entry) == 0;
+}
+
+bool glsl_symbol_table::add_type(const char *name, const glsl_type *t)
+{
+ symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(t);
+ return _mesa_symbol_table_add_symbol(table, -1, name, entry) == 0;
+}
+
+bool glsl_symbol_table::add_function(ir_function *f)
+{
+ if (this->language_version == 110 && name_declared_this_scope(f->name)) {
+ /* In 1.10, functions and variables have separate namespaces. */
+ symbol_table_entry *existing = get_entry(f->name);
+ if ((existing->f == NULL) && (existing->t == NULL)) {
+ existing->f = f;
+ return true;
+ }
+ }
+ symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f);
+ return _mesa_symbol_table_add_symbol(table, -1, f->name, entry) == 0;
+}
+
+void glsl_symbol_table::add_global_function(ir_function *f)
+{
+ symbol_table_entry *entry = new(mem_ctx) symbol_table_entry(f);
+ int added = _mesa_symbol_table_add_global_symbol(table, -1, f->name, entry);
+ assert(added == 0);
+ (void)added;
+}
+
+ir_variable *glsl_symbol_table::get_variable(const char *name)
+{
+ symbol_table_entry *entry = get_entry(name);
+ return entry != NULL ? entry->v : NULL;
+}
+
+const glsl_type *glsl_symbol_table::get_type(const char *name)
+{
+ symbol_table_entry *entry = get_entry(name);
+ return entry != NULL ? entry->t : NULL;
+}
+
+ir_function *glsl_symbol_table::get_function(const char *name)
+{
+ symbol_table_entry *entry = get_entry(name);
+ return entry != NULL ? entry->f : NULL;
+}
+
+symbol_table_entry *glsl_symbol_table::get_entry(const char *name)
+{
+ return (symbol_table_entry *)
+ _mesa_symbol_table_find_symbol(table, -1, name);
+}
diff --git a/mesalib/src/glsl/glsl_symbol_table.h b/mesalib/src/glsl/glsl_symbol_table.h index 637bc033b..1afeddb33 100644 --- a/mesalib/src/glsl/glsl_symbol_table.h +++ b/mesalib/src/glsl/glsl_symbol_table.h @@ -68,6 +68,11 @@ public: /* If the user *does* call delete, that's OK, we will just * ralloc_free in that case. Here, C++ will have already called the * destructor so tell ralloc not to do that again. */ + static void operator delete(void *table, void *ctx) + { + ralloc_set_destructor(table, NULL); + ralloc_free(table); + } static void operator delete(void *table) { ralloc_set_destructor(table, NULL); diff --git a/mesalib/src/glsl/glsl_types.cpp b/mesalib/src/glsl/glsl_types.cpp index 78d10bd93..ba862dfd6 100644 --- a/mesalib/src/glsl/glsl_types.cpp +++ b/mesalib/src/glsl/glsl_types.cpp @@ -1,509 +1,509 @@ -/* - * Copyright © 2009 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include "main/core.h" /* for Elements */ -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "glsl_types.h" -#include "builtin_types.h" -extern "C" { -#include "program/hash_table.h" -} - -hash_table *glsl_type::array_types = NULL; -hash_table *glsl_type::record_types = NULL; -void *glsl_type::mem_ctx = NULL; - -void -glsl_type::init_ralloc_type_ctx(void) -{ - if (glsl_type::mem_ctx == NULL) { - glsl_type::mem_ctx = ralloc_autofree_context(); - assert(glsl_type::mem_ctx != NULL); - } -} - -glsl_type::glsl_type(GLenum gl_type, - glsl_base_type base_type, unsigned vector_elements, - unsigned matrix_columns, const char *name) : - gl_type(gl_type), - base_type(base_type), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(vector_elements), matrix_columns(matrix_columns), - length(0) -{ - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - /* Neither dimension is zero or both dimensions are zero. - */ - assert((vector_elements == 0) == (matrix_columns == 0)); - memset(& fields, 0, sizeof(fields)); -} - -glsl_type::glsl_type(GLenum gl_type, - enum glsl_sampler_dim dim, bool shadow, bool array, - unsigned type, const char *name) : - gl_type(gl_type), - base_type(GLSL_TYPE_SAMPLER), - sampler_dimensionality(dim), sampler_shadow(shadow), - sampler_array(array), sampler_type(type), - vector_elements(0), matrix_columns(0), - length(0) -{ - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - memset(& fields, 0, sizeof(fields)); -} - -glsl_type::glsl_type(const glsl_struct_field *fields, unsigned num_fields, - const char *name) : - base_type(GLSL_TYPE_STRUCT), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(0), matrix_columns(0), - length(num_fields) -{ - unsigned int i; - - init_ralloc_type_ctx(); - this->name = ralloc_strdup(this->mem_ctx, name); - this->fields.structure = ralloc_array(this->mem_ctx, - glsl_struct_field, length); - for (i = 0; i < length; i++) { - this->fields.structure[i].type = fields[i].type; - this->fields.structure[i].name = ralloc_strdup(this->fields.structure, - fields[i].name); - } -} - -static void -add_types_to_symbol_table(glsl_symbol_table *symtab, - const struct glsl_type *types, - unsigned num_types, bool warn) -{ - (void) warn; - - for (unsigned i = 0; i < num_types; i++) { - symtab->add_type(types[i].name, & types[i]); - } -} - -void -glsl_type::generate_100ES_types(glsl_symbol_table *symtab) -{ - add_types_to_symbol_table(symtab, builtin_core_types, - Elements(builtin_core_types), - false); - add_types_to_symbol_table(symtab, builtin_structure_types, - Elements(builtin_structure_types), - false); - add_types_to_symbol_table(symtab, void_type, 1, false); -} - -void -glsl_type::generate_110_types(glsl_symbol_table *symtab) -{ - generate_100ES_types(symtab); - - add_types_to_symbol_table(symtab, builtin_110_types, - Elements(builtin_110_types), - false); - add_types_to_symbol_table(symtab, &_sampler3D_type, 1, false); - add_types_to_symbol_table(symtab, builtin_110_deprecated_structure_types, - Elements(builtin_110_deprecated_structure_types), - false); -} - - -void -glsl_type::generate_120_types(glsl_symbol_table *symtab) -{ - generate_110_types(symtab); - - add_types_to_symbol_table(symtab, builtin_120_types, - Elements(builtin_120_types), false); -} - - -void -glsl_type::generate_130_types(glsl_symbol_table *symtab) -{ - generate_120_types(symtab); - - add_types_to_symbol_table(symtab, builtin_130_types, - Elements(builtin_130_types), false); - generate_EXT_texture_array_types(symtab, false); -} - - -void -glsl_type::generate_ARB_texture_rectangle_types(glsl_symbol_table *symtab, - bool warn) -{ - add_types_to_symbol_table(symtab, builtin_ARB_texture_rectangle_types, - Elements(builtin_ARB_texture_rectangle_types), - warn); -} - - -void -glsl_type::generate_EXT_texture_array_types(glsl_symbol_table *symtab, - bool warn) -{ - add_types_to_symbol_table(symtab, builtin_EXT_texture_array_types, - Elements(builtin_EXT_texture_array_types), - warn); -} - - -void -glsl_type::generate_OES_texture_3D_types(glsl_symbol_table *symtab, bool warn) -{ - add_types_to_symbol_table(symtab, &_sampler3D_type, 1, warn); -} - - -void -_mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state) -{ - switch (state->language_version) { - case 100: - assert(state->es_shader); - glsl_type::generate_100ES_types(state->symbols); - break; - case 110: - glsl_type::generate_110_types(state->symbols); - break; - case 120: - glsl_type::generate_120_types(state->symbols); - break; - case 130: - glsl_type::generate_130_types(state->symbols); - break; - default: - /* error */ - break; - } - - if (state->ARB_texture_rectangle_enable) { - glsl_type::generate_ARB_texture_rectangle_types(state->symbols, - state->ARB_texture_rectangle_warn); - } - if (state->OES_texture_3D_enable && state->language_version == 100) { - glsl_type::generate_OES_texture_3D_types(state->symbols, - state->OES_texture_3D_warn); - } - - if (state->EXT_texture_array_enable && state->language_version < 130) { - // These are already included in 130; don't create twice. - glsl_type::generate_EXT_texture_array_types(state->symbols, - state->EXT_texture_array_warn); - } -} - - -const glsl_type *glsl_type::get_base_type() const -{ - switch (base_type) { - case GLSL_TYPE_UINT: - return uint_type; - case GLSL_TYPE_INT: - return int_type; - case GLSL_TYPE_FLOAT: - return float_type; - case GLSL_TYPE_BOOL: - return bool_type; - default: - return error_type; - } -} - - -void -_mesa_glsl_release_types(void) -{ - if (glsl_type::array_types != NULL) { - hash_table_dtor(glsl_type::array_types); - glsl_type::array_types = NULL; - } - - if (glsl_type::record_types != NULL) { - hash_table_dtor(glsl_type::record_types); - glsl_type::record_types = NULL; - } -} - - -glsl_type::glsl_type(const glsl_type *array, unsigned length) : - base_type(GLSL_TYPE_ARRAY), - sampler_dimensionality(0), sampler_shadow(0), sampler_array(0), - sampler_type(0), - vector_elements(0), matrix_columns(0), - name(NULL), length(length) -{ - this->fields.array = array; - /* Inherit the gl type of the base. The GL type is used for - * uniform/statevar handling in Mesa and the arrayness of the type - * is represented by the size rather than the type. - */ - this->gl_type = array->gl_type; - - /* Allow a maximum of 10 characters for the array size. This is enough - * for 32-bits of ~0. The extra 3 are for the '[', ']', and terminating - * NUL. - */ - const unsigned name_length = strlen(array->name) + 10 + 3; - char *const n = (char *) ralloc_size(this->mem_ctx, name_length); - - if (length == 0) - snprintf(n, name_length, "%s[]", array->name); - else - snprintf(n, name_length, "%s[%u]", array->name, length); - - this->name = n; -} - - -const glsl_type * -glsl_type::get_instance(unsigned base_type, unsigned rows, unsigned columns) -{ - if (base_type == GLSL_TYPE_VOID) - return void_type; - - if ((rows < 1) || (rows > 4) || (columns < 1) || (columns > 4)) - return error_type; - - /* Treat GLSL vectors as Nx1 matrices. - */ - if (columns == 1) { - switch (base_type) { - case GLSL_TYPE_UINT: - return uint_type + (rows - 1); - case GLSL_TYPE_INT: - return int_type + (rows - 1); - case GLSL_TYPE_FLOAT: - return float_type + (rows - 1); - case GLSL_TYPE_BOOL: - return bool_type + (rows - 1); - default: - return error_type; - } - } else { - if ((base_type != GLSL_TYPE_FLOAT) || (rows == 1)) - return error_type; - - /* GLSL matrix types are named mat{COLUMNS}x{ROWS}. Only the following - * combinations are valid: - * - * 1 2 3 4 - * 1 - * 2 x x x - * 3 x x x - * 4 x x x - */ -#define IDX(c,r) (((c-1)*3) + (r-1)) - - switch (IDX(columns, rows)) { - case IDX(2,2): return mat2_type; - case IDX(2,3): return mat2x3_type; - case IDX(2,4): return mat2x4_type; - case IDX(3,2): return mat3x2_type; - case IDX(3,3): return mat3_type; - case IDX(3,4): return mat3x4_type; - case IDX(4,2): return mat4x2_type; - case IDX(4,3): return mat4x3_type; - case IDX(4,4): return mat4_type; - default: return error_type; - } - } - - assert(!"Should not get here."); - return error_type; -} - - -const glsl_type * -glsl_type::get_array_instance(const glsl_type *base, unsigned array_size) -{ - - if (array_types == NULL) { - array_types = hash_table_ctor(64, hash_table_string_hash, - hash_table_string_compare); - } - - /* Generate a name using the base type pointer in the key. This is - * done because the name of the base type may not be unique across - * shaders. For example, two shaders may have different record types - * named 'foo'. - */ - char key[128]; - snprintf(key, sizeof(key), "%p[%u]", (void *) base, array_size); - - const glsl_type *t = (glsl_type *) hash_table_find(array_types, key); - if (t == NULL) { - t = new glsl_type(base, array_size); - - hash_table_insert(array_types, (void *) t, ralloc_strdup(mem_ctx, key)); - } - - assert(t->base_type == GLSL_TYPE_ARRAY); - assert(t->length == array_size); - assert(t->fields.array == base); - - return t; -} - - -int -glsl_type::record_key_compare(const void *a, const void *b) -{ - const glsl_type *const key1 = (glsl_type *) a; - const glsl_type *const key2 = (glsl_type *) b; - - /* Return zero is the types match (there is zero difference) or non-zero - * otherwise. - */ - if (strcmp(key1->name, key2->name) != 0) - return 1; - - if (key1->length != key2->length) - return 1; - - for (unsigned i = 0; i < key1->length; i++) { - if (key1->fields.structure[i].type != key2->fields.structure[i].type) - return 1; - if (strcmp(key1->fields.structure[i].name, - key2->fields.structure[i].name) != 0) - return 1; - } - - return 0; -} - - -unsigned -glsl_type::record_key_hash(const void *a) -{ - const glsl_type *const key = (glsl_type *) a; - char hash_key[128]; - unsigned size = 0; - - size = snprintf(hash_key, sizeof(hash_key), "%08x", key->length); - - for (unsigned i = 0; i < key->length; i++) { - if (size >= sizeof(hash_key)) - break; - - size += snprintf(& hash_key[size], sizeof(hash_key) - size, - "%p", (void *) key->fields.structure[i].type); - } - - return hash_table_string_hash(& hash_key); -} - - -const glsl_type * -glsl_type::get_record_instance(const glsl_struct_field *fields, - unsigned num_fields, - const char *name) -{ - const glsl_type key(fields, num_fields, name); - - if (record_types == NULL) { - record_types = hash_table_ctor(64, record_key_hash, record_key_compare); - } - - const glsl_type *t = (glsl_type *) hash_table_find(record_types, & key); - if (t == NULL) { - t = new glsl_type(fields, num_fields, name); - - hash_table_insert(record_types, (void *) t, t); - } - - assert(t->base_type == GLSL_TYPE_STRUCT); - assert(t->length == num_fields); - assert(strcmp(t->name, name) == 0); - - return t; -} - - -const glsl_type * -glsl_type::field_type(const char *name) const -{ - if (this->base_type != GLSL_TYPE_STRUCT) - return error_type; - - for (unsigned i = 0; i < this->length; i++) { - if (strcmp(name, this->fields.structure[i].name) == 0) - return this->fields.structure[i].type; - } - - return error_type; -} - - -int -glsl_type::field_index(const char *name) const -{ - if (this->base_type != GLSL_TYPE_STRUCT) - return -1; - - for (unsigned i = 0; i < this->length; i++) { - if (strcmp(name, this->fields.structure[i].name) == 0) - return i; - } - - return -1; -} - - -unsigned -glsl_type::component_slots() const -{ - switch (this->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_FLOAT: - case GLSL_TYPE_BOOL: - return this->components(); - - case GLSL_TYPE_STRUCT: { - unsigned size = 0; - - for (unsigned i = 0; i < this->length; i++) - size += this->fields.structure[i].type->component_slots(); - - return size; - } - - case GLSL_TYPE_ARRAY: - return this->length * this->fields.array->component_slots(); - - default: - return 0; - } -} +/*
+ * Copyright © 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "main/core.h" /* for Elements */
+#include "glsl_symbol_table.h"
+#include "glsl_parser_extras.h"
+#include "glsl_types.h"
+#include "builtin_types.h"
+extern "C" {
+#include "program/hash_table.h"
+}
+
+hash_table *glsl_type::array_types = NULL;
+hash_table *glsl_type::record_types = NULL;
+void *glsl_type::mem_ctx = NULL;
+
+void
+glsl_type::init_ralloc_type_ctx(void)
+{
+ if (glsl_type::mem_ctx == NULL) {
+ glsl_type::mem_ctx = ralloc_autofree_context();
+ assert(glsl_type::mem_ctx != NULL);
+ }
+}
+
+glsl_type::glsl_type(GLenum gl_type,
+ glsl_base_type base_type, unsigned vector_elements,
+ unsigned matrix_columns, const char *name) :
+ gl_type(gl_type),
+ base_type(base_type),
+ sampler_dimensionality(0), sampler_shadow(0), sampler_array(0),
+ sampler_type(0),
+ vector_elements(vector_elements), matrix_columns(matrix_columns),
+ length(0)
+{
+ init_ralloc_type_ctx();
+ this->name = ralloc_strdup(this->mem_ctx, name);
+ /* Neither dimension is zero or both dimensions are zero.
+ */
+ assert((vector_elements == 0) == (matrix_columns == 0));
+ memset(& fields, 0, sizeof(fields));
+}
+
+glsl_type::glsl_type(GLenum gl_type,
+ enum glsl_sampler_dim dim, bool shadow, bool array,
+ unsigned type, const char *name) :
+ gl_type(gl_type),
+ base_type(GLSL_TYPE_SAMPLER),
+ sampler_dimensionality(dim), sampler_shadow(shadow),
+ sampler_array(array), sampler_type(type),
+ vector_elements(0), matrix_columns(0),
+ length(0)
+{
+ init_ralloc_type_ctx();
+ this->name = ralloc_strdup(this->mem_ctx, name);
+ memset(& fields, 0, sizeof(fields));
+}
+
+glsl_type::glsl_type(const glsl_struct_field *fields, unsigned num_fields,
+ const char *name) :
+ base_type(GLSL_TYPE_STRUCT),
+ sampler_dimensionality(0), sampler_shadow(0), sampler_array(0),
+ sampler_type(0),
+ vector_elements(0), matrix_columns(0),
+ length(num_fields)
+{
+ unsigned int i;
+
+ init_ralloc_type_ctx();
+ this->name = ralloc_strdup(this->mem_ctx, name);
+ this->fields.structure = ralloc_array(this->mem_ctx,
+ glsl_struct_field, length);
+ for (i = 0; i < length; i++) {
+ this->fields.structure[i].type = fields[i].type;
+ this->fields.structure[i].name = ralloc_strdup(this->fields.structure,
+ fields[i].name);
+ }
+}
+
+static void
+add_types_to_symbol_table(glsl_symbol_table *symtab,
+ const struct glsl_type *types,
+ unsigned num_types, bool warn)
+{
+ (void) warn;
+
+ for (unsigned i = 0; i < num_types; i++) {
+ symtab->add_type(types[i].name, & types[i]);
+ }
+}
+
+void
+glsl_type::generate_100ES_types(glsl_symbol_table *symtab)
+{
+ add_types_to_symbol_table(symtab, builtin_core_types,
+ Elements(builtin_core_types),
+ false);
+ add_types_to_symbol_table(symtab, builtin_structure_types,
+ Elements(builtin_structure_types),
+ false);
+ add_types_to_symbol_table(symtab, void_type, 1, false);
+}
+
+void
+glsl_type::generate_110_types(glsl_symbol_table *symtab)
+{
+ generate_100ES_types(symtab);
+
+ add_types_to_symbol_table(symtab, builtin_110_types,
+ Elements(builtin_110_types),
+ false);
+ add_types_to_symbol_table(symtab, &_sampler3D_type, 1, false);
+ add_types_to_symbol_table(symtab, builtin_110_deprecated_structure_types,
+ Elements(builtin_110_deprecated_structure_types),
+ false);
+}
+
+
+void
+glsl_type::generate_120_types(glsl_symbol_table *symtab)
+{
+ generate_110_types(symtab);
+
+ add_types_to_symbol_table(symtab, builtin_120_types,
+ Elements(builtin_120_types), false);
+}
+
+
+void
+glsl_type::generate_130_types(glsl_symbol_table *symtab)
+{
+ generate_120_types(symtab);
+
+ add_types_to_symbol_table(symtab, builtin_130_types,
+ Elements(builtin_130_types), false);
+ generate_EXT_texture_array_types(symtab, false);
+}
+
+
+void
+glsl_type::generate_ARB_texture_rectangle_types(glsl_symbol_table *symtab,
+ bool warn)
+{
+ add_types_to_symbol_table(symtab, builtin_ARB_texture_rectangle_types,
+ Elements(builtin_ARB_texture_rectangle_types),
+ warn);
+}
+
+
+void
+glsl_type::generate_EXT_texture_array_types(glsl_symbol_table *symtab,
+ bool warn)
+{
+ add_types_to_symbol_table(symtab, builtin_EXT_texture_array_types,
+ Elements(builtin_EXT_texture_array_types),
+ warn);
+}
+
+
+void
+glsl_type::generate_OES_texture_3D_types(glsl_symbol_table *symtab, bool warn)
+{
+ add_types_to_symbol_table(symtab, &_sampler3D_type, 1, warn);
+}
+
+
+void
+_mesa_glsl_initialize_types(struct _mesa_glsl_parse_state *state)
+{
+ switch (state->language_version) {
+ case 100:
+ assert(state->es_shader);
+ glsl_type::generate_100ES_types(state->symbols);
+ break;
+ case 110:
+ glsl_type::generate_110_types(state->symbols);
+ break;
+ case 120:
+ glsl_type::generate_120_types(state->symbols);
+ break;
+ case 130:
+ glsl_type::generate_130_types(state->symbols);
+ break;
+ default:
+ /* error */
+ break;
+ }
+
+ if (state->ARB_texture_rectangle_enable) {
+ glsl_type::generate_ARB_texture_rectangle_types(state->symbols,
+ state->ARB_texture_rectangle_warn);
+ }
+ if (state->OES_texture_3D_enable && state->language_version == 100) {
+ glsl_type::generate_OES_texture_3D_types(state->symbols,
+ state->OES_texture_3D_warn);
+ }
+
+ if (state->EXT_texture_array_enable && state->language_version < 130) {
+ // These are already included in 130; don't create twice.
+ glsl_type::generate_EXT_texture_array_types(state->symbols,
+ state->EXT_texture_array_warn);
+ }
+}
+
+
+const glsl_type *glsl_type::get_base_type() const
+{
+ switch (base_type) {
+ case GLSL_TYPE_UINT:
+ return uint_type;
+ case GLSL_TYPE_INT:
+ return int_type;
+ case GLSL_TYPE_FLOAT:
+ return float_type;
+ case GLSL_TYPE_BOOL:
+ return bool_type;
+ default:
+ return error_type;
+ }
+}
+
+
+void
+_mesa_glsl_release_types(void)
+{
+ if (glsl_type::array_types != NULL) {
+ hash_table_dtor(glsl_type::array_types);
+ glsl_type::array_types = NULL;
+ }
+
+ if (glsl_type::record_types != NULL) {
+ hash_table_dtor(glsl_type::record_types);
+ glsl_type::record_types = NULL;
+ }
+}
+
+
+glsl_type::glsl_type(const glsl_type *array, unsigned length) :
+ base_type(GLSL_TYPE_ARRAY),
+ sampler_dimensionality(0), sampler_shadow(0), sampler_array(0),
+ sampler_type(0),
+ vector_elements(0), matrix_columns(0),
+ name(NULL), length(length)
+{
+ this->fields.array = array;
+ /* Inherit the gl type of the base. The GL type is used for
+ * uniform/statevar handling in Mesa and the arrayness of the type
+ * is represented by the size rather than the type.
+ */
+ this->gl_type = array->gl_type;
+
+ /* Allow a maximum of 10 characters for the array size. This is enough
+ * for 32-bits of ~0. The extra 3 are for the '[', ']', and terminating
+ * NUL.
+ */
+ const unsigned name_length = strlen(array->name) + 10 + 3;
+ char *const n = (char *) ralloc_size(this->mem_ctx, name_length);
+
+ if (length == 0)
+ snprintf(n, name_length, "%s[]", array->name);
+ else
+ snprintf(n, name_length, "%s[%u]", array->name, length);
+
+ this->name = n;
+}
+
+
+const glsl_type *
+glsl_type::get_instance(unsigned base_type, unsigned rows, unsigned columns)
+{
+ if (base_type == GLSL_TYPE_VOID)
+ return void_type;
+
+ if ((rows < 1) || (rows > 4) || (columns < 1) || (columns > 4))
+ return error_type;
+
+ /* Treat GLSL vectors as Nx1 matrices.
+ */
+ if (columns == 1) {
+ switch (base_type) {
+ case GLSL_TYPE_UINT:
+ return uint_type + (rows - 1);
+ case GLSL_TYPE_INT:
+ return int_type + (rows - 1);
+ case GLSL_TYPE_FLOAT:
+ return float_type + (rows - 1);
+ case GLSL_TYPE_BOOL:
+ return bool_type + (rows - 1);
+ default:
+ return error_type;
+ }
+ } else {
+ if ((base_type != GLSL_TYPE_FLOAT) || (rows == 1))
+ return error_type;
+
+ /* GLSL matrix types are named mat{COLUMNS}x{ROWS}. Only the following
+ * combinations are valid:
+ *
+ * 1 2 3 4
+ * 1
+ * 2 x x x
+ * 3 x x x
+ * 4 x x x
+ */
+#define IDX(c,r) (((c-1)*3) + (r-1))
+
+ switch (IDX(columns, rows)) {
+ case IDX(2,2): return mat2_type;
+ case IDX(2,3): return mat2x3_type;
+ case IDX(2,4): return mat2x4_type;
+ case IDX(3,2): return mat3x2_type;
+ case IDX(3,3): return mat3_type;
+ case IDX(3,4): return mat3x4_type;
+ case IDX(4,2): return mat4x2_type;
+ case IDX(4,3): return mat4x3_type;
+ case IDX(4,4): return mat4_type;
+ default: return error_type;
+ }
+ }
+
+ assert(!"Should not get here.");
+ return error_type;
+}
+
+
+const glsl_type *
+glsl_type::get_array_instance(const glsl_type *base, unsigned array_size)
+{
+
+ if (array_types == NULL) {
+ array_types = hash_table_ctor(64, hash_table_string_hash,
+ hash_table_string_compare);
+ }
+
+ /* Generate a name using the base type pointer in the key. This is
+ * done because the name of the base type may not be unique across
+ * shaders. For example, two shaders may have different record types
+ * named 'foo'.
+ */
+ char key[128];
+ snprintf(key, sizeof(key), "%p[%u]", (void *) base, array_size);
+
+ const glsl_type *t = (glsl_type *) hash_table_find(array_types, key);
+ if (t == NULL) {
+ t = new glsl_type(base, array_size);
+
+ hash_table_insert(array_types, (void *) t, ralloc_strdup(mem_ctx, key));
+ }
+
+ assert(t->base_type == GLSL_TYPE_ARRAY);
+ assert(t->length == array_size);
+ assert(t->fields.array == base);
+
+ return t;
+}
+
+
+int
+glsl_type::record_key_compare(const void *a, const void *b)
+{
+ const glsl_type *const key1 = (glsl_type *) a;
+ const glsl_type *const key2 = (glsl_type *) b;
+
+ /* Return zero is the types match (there is zero difference) or non-zero
+ * otherwise.
+ */
+ if (strcmp(key1->name, key2->name) != 0)
+ return 1;
+
+ if (key1->length != key2->length)
+ return 1;
+
+ for (unsigned i = 0; i < key1->length; i++) {
+ if (key1->fields.structure[i].type != key2->fields.structure[i].type)
+ return 1;
+ if (strcmp(key1->fields.structure[i].name,
+ key2->fields.structure[i].name) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+unsigned
+glsl_type::record_key_hash(const void *a)
+{
+ const glsl_type *const key = (glsl_type *) a;
+ char hash_key[128];
+ unsigned size = 0;
+
+ size = snprintf(hash_key, sizeof(hash_key), "%08x", key->length);
+
+ for (unsigned i = 0; i < key->length; i++) {
+ if (size >= sizeof(hash_key))
+ break;
+
+ size += snprintf(& hash_key[size], sizeof(hash_key) - size,
+ "%p", (void *) key->fields.structure[i].type);
+ }
+
+ return hash_table_string_hash(& hash_key);
+}
+
+
+const glsl_type *
+glsl_type::get_record_instance(const glsl_struct_field *fields,
+ unsigned num_fields,
+ const char *name)
+{
+ const glsl_type key(fields, num_fields, name);
+
+ if (record_types == NULL) {
+ record_types = hash_table_ctor(64, record_key_hash, record_key_compare);
+ }
+
+ const glsl_type *t = (glsl_type *) hash_table_find(record_types, & key);
+ if (t == NULL) {
+ t = new glsl_type(fields, num_fields, name);
+
+ hash_table_insert(record_types, (void *) t, t);
+ }
+
+ assert(t->base_type == GLSL_TYPE_STRUCT);
+ assert(t->length == num_fields);
+ assert(strcmp(t->name, name) == 0);
+
+ return t;
+}
+
+
+const glsl_type *
+glsl_type::field_type(const char *name) const
+{
+ if (this->base_type != GLSL_TYPE_STRUCT)
+ return error_type;
+
+ for (unsigned i = 0; i < this->length; i++) {
+ if (strcmp(name, this->fields.structure[i].name) == 0)
+ return this->fields.structure[i].type;
+ }
+
+ return error_type;
+}
+
+
+int
+glsl_type::field_index(const char *name) const
+{
+ if (this->base_type != GLSL_TYPE_STRUCT)
+ return -1;
+
+ for (unsigned i = 0; i < this->length; i++) {
+ if (strcmp(name, this->fields.structure[i].name) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
+unsigned
+glsl_type::component_slots() const
+{
+ switch (this->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ case GLSL_TYPE_FLOAT:
+ case GLSL_TYPE_BOOL:
+ return this->components();
+
+ case GLSL_TYPE_STRUCT: {
+ unsigned size = 0;
+
+ for (unsigned i = 0; i < this->length; i++)
+ size += this->fields.structure[i].type->component_slots();
+
+ return size;
+ }
+
+ case GLSL_TYPE_ARRAY:
+ return this->length * this->fields.array->component_slots();
+
+ default:
+ return 0;
+ }
+}
diff --git a/mesalib/src/glsl/hir_field_selection.cpp b/mesalib/src/glsl/hir_field_selection.cpp index 3c33127b5..21f107405 100644 --- a/mesalib/src/glsl/hir_field_selection.cpp +++ b/mesalib/src/glsl/hir_field_selection.cpp @@ -1,102 +1,102 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "ir.h" -#include "program/symbol_table.h" -#include "glsl_parser_extras.h" -#include "ast.h" -#include "glsl_types.h" - -ir_rvalue * -_mesa_ast_field_selection_to_hir(const ast_expression *expr, - exec_list *instructions, - struct _mesa_glsl_parse_state *state) -{ - void *ctx = state; - ir_rvalue *result = NULL; - ir_rvalue *op; - - op = expr->subexpressions[0]->hir(instructions, state); - - /* There are two kinds of field selection. There is the selection of a - * specific field from a structure, and there is the selection of a - * swizzle / mask from a vector. Which is which is determined entirely - * by the base type of the thing to which the field selection operator is - * being applied. - */ - YYLTYPE loc = expr->get_location(); - if (op->type->is_error()) { - /* silently propagate the error */ - } else if (op->type->is_vector()) { - ir_swizzle *swiz = ir_swizzle::create(op, - expr->primary_expression.identifier, - op->type->vector_elements); - if (swiz != NULL) { - result = swiz; - } else { - /* FINISHME: Logging of error messages should be moved into - * FINISHME: ir_swizzle::create. This allows the generation of more - * FINISHME: specific error messages. - */ - _mesa_glsl_error(& loc, state, "Invalid swizzle / mask `%s'", - expr->primary_expression.identifier); - } - } else if (op->type->base_type == GLSL_TYPE_STRUCT) { - result = new(ctx) ir_dereference_record(op, - expr->primary_expression.identifier); - - if (result->type->is_error()) { - _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " - "structure", - expr->primary_expression.identifier); - } - } else if (expr->subexpressions[1] != NULL) { - /* Handle "method calls" in GLSL 1.20 - namely, array.length() */ - if (state->language_version < 120) - _mesa_glsl_error(&loc, state, "Methods not supported in GLSL 1.10."); - - ast_expression *call = expr->subexpressions[1]; - assert(call->oper == ast_function_call); - - const char *method; - method = call->subexpressions[0]->primary_expression.identifier; - - if (op->type->is_array() && strcmp(method, "length") == 0) { - if (!call->expressions.is_empty()) - _mesa_glsl_error(&loc, state, "length method takes no arguments."); - - if (op->type->array_size() == 0) - _mesa_glsl_error(&loc, state, "length called on unsized array."); - - result = new(ctx) ir_constant(op->type->array_size()); - } else { - _mesa_glsl_error(&loc, state, "Unknown method: `%s'.", method); - } - } else { - _mesa_glsl_error(& loc, state, "Cannot access field `%s' of " - "non-structure / non-vector.", - expr->primary_expression.identifier); - } - - return result ? result : ir_call::get_error_instruction(ctx); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "ir.h"
+#include "program/symbol_table.h"
+#include "glsl_parser_extras.h"
+#include "ast.h"
+#include "glsl_types.h"
+
+ir_rvalue *
+_mesa_ast_field_selection_to_hir(const ast_expression *expr,
+ exec_list *instructions,
+ struct _mesa_glsl_parse_state *state)
+{
+ void *ctx = state;
+ ir_rvalue *result = NULL;
+ ir_rvalue *op;
+
+ op = expr->subexpressions[0]->hir(instructions, state);
+
+ /* There are two kinds of field selection. There is the selection of a
+ * specific field from a structure, and there is the selection of a
+ * swizzle / mask from a vector. Which is which is determined entirely
+ * by the base type of the thing to which the field selection operator is
+ * being applied.
+ */
+ YYLTYPE loc = expr->get_location();
+ if (op->type->is_error()) {
+ /* silently propagate the error */
+ } else if (op->type->is_vector()) {
+ ir_swizzle *swiz = ir_swizzle::create(op,
+ expr->primary_expression.identifier,
+ op->type->vector_elements);
+ if (swiz != NULL) {
+ result = swiz;
+ } else {
+ /* FINISHME: Logging of error messages should be moved into
+ * FINISHME: ir_swizzle::create. This allows the generation of more
+ * FINISHME: specific error messages.
+ */
+ _mesa_glsl_error(& loc, state, "Invalid swizzle / mask `%s'",
+ expr->primary_expression.identifier);
+ }
+ } else if (op->type->base_type == GLSL_TYPE_STRUCT) {
+ result = new(ctx) ir_dereference_record(op,
+ expr->primary_expression.identifier);
+
+ if (result->type->is_error()) {
+ _mesa_glsl_error(& loc, state, "Cannot access field `%s' of "
+ "structure",
+ expr->primary_expression.identifier);
+ }
+ } else if (expr->subexpressions[1] != NULL) {
+ /* Handle "method calls" in GLSL 1.20 - namely, array.length() */
+ if (state->language_version < 120)
+ _mesa_glsl_error(&loc, state, "Methods not supported in GLSL 1.10.");
+
+ ast_expression *call = expr->subexpressions[1];
+ assert(call->oper == ast_function_call);
+
+ const char *method;
+ method = call->subexpressions[0]->primary_expression.identifier;
+
+ if (op->type->is_array() && strcmp(method, "length") == 0) {
+ if (!call->expressions.is_empty())
+ _mesa_glsl_error(&loc, state, "length method takes no arguments.");
+
+ if (op->type->array_size() == 0)
+ _mesa_glsl_error(&loc, state, "length called on unsized array.");
+
+ result = new(ctx) ir_constant(op->type->array_size());
+ } else {
+ _mesa_glsl_error(&loc, state, "Unknown method: `%s'.", method);
+ }
+ } else {
+ _mesa_glsl_error(& loc, state, "Cannot access field `%s' of "
+ "non-structure / non-vector.",
+ expr->primary_expression.identifier);
+ }
+
+ return result ? result : ir_call::get_error_instruction(ctx);
+}
diff --git a/mesalib/src/glsl/ir.cpp b/mesalib/src/glsl/ir.cpp index a3623b31e..7ee07f3c6 100644 --- a/mesalib/src/glsl/ir.cpp +++ b/mesalib/src/glsl/ir.cpp @@ -1,1572 +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 <string.h> -#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 == (int) type->base_type); - if (sampler->type->sampler_shadow) - assert(type->vector_elements == 4 || type->vector_elements == 1); - else - assert(type->vector_elements == 4); -} - - -void -ir_swizzle::init_mask(const unsigned *comp, unsigned count) -{ - assert((count >= 1) && (count <= 4)); - - memset(&this->mask, 0, sizeof(this->mask)); - this->mask.num_components = count; - - unsigned dup_mask = 0; - switch (count) { - case 4: - assert(comp[3] <= 3); - dup_mask |= (1U << comp[3]) - & ((1U << comp[0]) | (1U << comp[1]) | (1U << comp[2])); - this->mask.w = comp[3]; - - case 3: - assert(comp[2] <= 3); - dup_mask |= (1U << comp[2]) - & ((1U << comp[0]) | (1U << comp[1])); - this->mask.z = comp[2]; - - case 2: - assert(comp[1] <= 3); - dup_mask |= (1U << comp[1]) - & ((1U << comp[0])); - this->mask.y = comp[1]; - - case 1: - assert(comp[0] <= 3); - this->mask.x = comp[0]; - } - - this->mask.has_duplicates = dup_mask != 0; - - /* Based on the number of elements in the swizzle and the base type - * (i.e., float, int, unsigned, or bool) of the vector being swizzled, - * generate the type of the resulting value. - */ - type = glsl_type::get_instance(val->type->base_type, mask.num_components, 1); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, unsigned x, unsigned y, unsigned z, - unsigned w, unsigned count) - : val(val) -{ - const unsigned components[4] = { x, y, z, w }; - this->ir_type = ir_type_swizzle; - this->init_mask(components, count); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, const unsigned *comp, - unsigned count) - : val(val) -{ - this->ir_type = ir_type_swizzle; - this->init_mask(comp, count); -} - -ir_swizzle::ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask) -{ - this->ir_type = ir_type_swizzle; - this->val = val; - this->mask = mask; - this->type = glsl_type::get_instance(val->type->base_type, - mask.num_components, 1); -} - -#define X 1 -#define R 5 -#define S 9 -#define I 13 - -ir_swizzle * -ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length) -{ - void *ctx = ralloc_parent(val); - - /* For each possible swizzle character, this table encodes the value in - * \c idx_map that represents the 0th element of the vector. For invalid - * swizzle characters (e.g., 'k'), a special value is used that will allow - * detection of errors. - */ - static const unsigned char base_idx[26] = { - /* a b c d e f g h i j k l m */ - R, R, I, I, I, I, R, I, I, I, I, I, I, - /* n o p q r s t u v w x y z */ - I, I, S, S, R, S, S, I, I, X, X, X, X - }; - - /* Each valid swizzle character has an entry in the previous table. This - * table encodes the base index encoded in the previous table plus the actual - * index of the swizzle character. When processing swizzles, the first - * character in the string is indexed in the previous table. Each character - * in the string is indexed in this table, and the value found there has the - * value form the first table subtracted. The result must be on the range - * [0,3]. - * - * For example, the string "wzyx" will get X from the first table. Each of - * the charcaters will get X+3, X+2, X+1, and X+0 from this table. After - * subtraction, the swizzle values are { 3, 2, 1, 0 }. - * - * The string "wzrg" will get X from the first table. Each of the characters - * will get X+3, X+2, R+0, and R+1 from this table. After subtraction, the - * swizzle values are { 3, 2, 4, 5 }. Since 4 and 5 are outside the range - * [0,3], the error is detected. - */ - static const unsigned char idx_map[26] = { - /* a b c d e f g h i j k l m */ - R+3, R+2, 0, 0, 0, 0, R+1, 0, 0, 0, 0, 0, 0, - /* n o p q r s t u v w x y z */ - 0, 0, S+2, S+3, R+0, S+0, S+1, 0, 0, X+3, X+0, X+1, X+2 - }; - - int swiz_idx[4] = { 0, 0, 0, 0 }; - unsigned i; - - - /* Validate the first character in the swizzle string and look up the base - * index value as described above. - */ - if ((str[0] < 'a') || (str[0] > 'z')) - return NULL; - - const unsigned base = base_idx[str[0] - 'a']; - - - for (i = 0; (i < 4) && (str[i] != '\0'); i++) { - /* Validate the next character, and, as described above, convert it to a - * swizzle index. - */ - if ((str[i] < 'a') || (str[i] > 'z')) - return NULL; - - swiz_idx[i] = idx_map[str[i] - 'a'] - base; - if ((swiz_idx[i] < 0) || (swiz_idx[i] >= (int) vector_length)) - return NULL; - } - - if (str[i] != '\0') - return NULL; - - return new(ctx) ir_swizzle(val, swiz_idx[0], swiz_idx[1], swiz_idx[2], - swiz_idx[3], i); -} - -#undef X -#undef R -#undef S -#undef I - -ir_variable * -ir_swizzle::variable_referenced() -{ - 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 <string.h>
+#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 == (int) type->base_type);
+ if (sampler->type->sampler_shadow)
+ assert(type->vector_elements == 4 || type->vector_elements == 1);
+ else
+ assert(type->vector_elements == 4);
+}
+
+
+void
+ir_swizzle::init_mask(const unsigned *comp, unsigned count)
+{
+ assert((count >= 1) && (count <= 4));
+
+ memset(&this->mask, 0, sizeof(this->mask));
+ this->mask.num_components = count;
+
+ unsigned dup_mask = 0;
+ switch (count) {
+ case 4:
+ assert(comp[3] <= 3);
+ dup_mask |= (1U << comp[3])
+ & ((1U << comp[0]) | (1U << comp[1]) | (1U << comp[2]));
+ this->mask.w = comp[3];
+
+ case 3:
+ assert(comp[2] <= 3);
+ dup_mask |= (1U << comp[2])
+ & ((1U << comp[0]) | (1U << comp[1]));
+ this->mask.z = comp[2];
+
+ case 2:
+ assert(comp[1] <= 3);
+ dup_mask |= (1U << comp[1])
+ & ((1U << comp[0]));
+ this->mask.y = comp[1];
+
+ case 1:
+ assert(comp[0] <= 3);
+ this->mask.x = comp[0];
+ }
+
+ this->mask.has_duplicates = dup_mask != 0;
+
+ /* Based on the number of elements in the swizzle and the base type
+ * (i.e., float, int, unsigned, or bool) of the vector being swizzled,
+ * generate the type of the resulting value.
+ */
+ type = glsl_type::get_instance(val->type->base_type, mask.num_components, 1);
+}
+
+ir_swizzle::ir_swizzle(ir_rvalue *val, unsigned x, unsigned y, unsigned z,
+ unsigned w, unsigned count)
+ : val(val)
+{
+ const unsigned components[4] = { x, y, z, w };
+ this->ir_type = ir_type_swizzle;
+ this->init_mask(components, count);
+}
+
+ir_swizzle::ir_swizzle(ir_rvalue *val, const unsigned *comp,
+ unsigned count)
+ : val(val)
+{
+ this->ir_type = ir_type_swizzle;
+ this->init_mask(comp, count);
+}
+
+ir_swizzle::ir_swizzle(ir_rvalue *val, ir_swizzle_mask mask)
+{
+ this->ir_type = ir_type_swizzle;
+ this->val = val;
+ this->mask = mask;
+ this->type = glsl_type::get_instance(val->type->base_type,
+ mask.num_components, 1);
+}
+
+#define X 1
+#define R 5
+#define S 9
+#define I 13
+
+ir_swizzle *
+ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length)
+{
+ void *ctx = ralloc_parent(val);
+
+ /* For each possible swizzle character, this table encodes the value in
+ * \c idx_map that represents the 0th element of the vector. For invalid
+ * swizzle characters (e.g., 'k'), a special value is used that will allow
+ * detection of errors.
+ */
+ static const unsigned char base_idx[26] = {
+ /* a b c d e f g h i j k l m */
+ R, R, I, I, I, I, R, I, I, I, I, I, I,
+ /* n o p q r s t u v w x y z */
+ I, I, S, S, R, S, S, I, I, X, X, X, X
+ };
+
+ /* Each valid swizzle character has an entry in the previous table. This
+ * table encodes the base index encoded in the previous table plus the actual
+ * index of the swizzle character. When processing swizzles, the first
+ * character in the string is indexed in the previous table. Each character
+ * in the string is indexed in this table, and the value found there has the
+ * value form the first table subtracted. The result must be on the range
+ * [0,3].
+ *
+ * For example, the string "wzyx" will get X from the first table. Each of
+ * the charcaters will get X+3, X+2, X+1, and X+0 from this table. After
+ * subtraction, the swizzle values are { 3, 2, 1, 0 }.
+ *
+ * The string "wzrg" will get X from the first table. Each of the characters
+ * will get X+3, X+2, R+0, and R+1 from this table. After subtraction, the
+ * swizzle values are { 3, 2, 4, 5 }. Since 4 and 5 are outside the range
+ * [0,3], the error is detected.
+ */
+ static const unsigned char idx_map[26] = {
+ /* a b c d e f g h i j k l m */
+ R+3, R+2, 0, 0, 0, 0, R+1, 0, 0, 0, 0, 0, 0,
+ /* n o p q r s t u v w x y z */
+ 0, 0, S+2, S+3, R+0, S+0, S+1, 0, 0, X+3, X+0, X+1, X+2
+ };
+
+ int swiz_idx[4] = { 0, 0, 0, 0 };
+ unsigned i;
+
+
+ /* Validate the first character in the swizzle string and look up the base
+ * index value as described above.
+ */
+ if ((str[0] < 'a') || (str[0] > 'z'))
+ return NULL;
+
+ const unsigned base = base_idx[str[0] - 'a'];
+
+
+ for (i = 0; (i < 4) && (str[i] != '\0'); i++) {
+ /* Validate the next character, and, as described above, convert it to a
+ * swizzle index.
+ */
+ if ((str[i] < 'a') || (str[i] > 'z'))
+ return NULL;
+
+ swiz_idx[i] = idx_map[str[i] - 'a'] - base;
+ if ((swiz_idx[i] < 0) || (swiz_idx[i] >= (int) vector_length))
+ return NULL;
+ }
+
+ if (str[i] != '\0')
+ return NULL;
+
+ return new(ctx) ir_swizzle(val, swiz_idx[0], swiz_idx[1], swiz_idx[2],
+ swiz_idx[3], i);
+}
+
+#undef X
+#undef R
+#undef S
+#undef I
+
+ir_variable *
+ir_swizzle::variable_referenced()
+{
+ 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_constant_expression.cpp b/mesalib/src/glsl/ir_constant_expression.cpp index 2a3084896..1d39e31fa 100644 --- a/mesalib/src/glsl/ir_constant_expression.cpp +++ b/mesalib/src/glsl/ir_constant_expression.cpp @@ -1,1372 +1,1372 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file ir_constant_expression.cpp - * Evaluate and process constant valued expressions - * - * In GLSL, constant valued expressions are used in several places. These - * must be processed and evaluated very early in the compilation process. - * - * * Sizes of arrays - * * Initializers for uniforms - * * Initializers for \c const variables - */ - -#include <math.h> -#include "main/core.h" /* for MAX2, MIN2, CLAMP */ -#include "ir.h" -#include "ir_visitor.h" -#include "glsl_types.h" - -static float -dot(ir_constant *op0, ir_constant *op1) -{ - assert(op0->type->is_float() && op1->type->is_float()); - - float result = 0; - for (unsigned c = 0; c < op0->type->components(); c++) - result += op0->value.f[c] * op1->value.f[c]; - - return result; -} - -ir_constant * -ir_expression::constant_expression_value() -{ - if (this->type->is_error()) - return NULL; - - ir_constant *op[Elements(this->operands)] = { NULL, }; - ir_constant_data data; - - memset(&data, 0, sizeof(data)); - - for (unsigned operand = 0; operand < this->get_num_operands(); operand++) { - op[operand] = this->operands[operand]->constant_expression_value(); - if (!op[operand]) - return NULL; - } - - if (op[1] != NULL) - assert(op[0]->type->base_type == op[1]->type->base_type); - - bool op0_scalar = op[0]->type->is_scalar(); - bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar(); - - /* When iterating over a vector or matrix's components, we want to increase - * the loop counter. However, for scalars, we want to stay at 0. - */ - unsigned c0_inc = op0_scalar ? 0 : 1; - unsigned c1_inc = op1_scalar ? 0 : 1; - unsigned components; - if (op1_scalar || !op[1]) { - components = op[0]->type->components(); - } else { - components = op[1]->type->components(); - } - - void *ctx = ralloc_parent(this); - - /* Handle array operations here, rather than below. */ - if (op[0]->type->is_array()) { - assert(op[1] != NULL && op[1]->type->is_array()); - switch (this->operation) { - case ir_binop_all_equal: - return new(ctx) ir_constant(op[0]->has_value(op[1])); - case ir_binop_any_nequal: - return new(ctx) ir_constant(!op[0]->has_value(op[1])); - default: - break; - } - return NULL; - } - - switch (this->operation) { - case ir_unop_bit_not: - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - for (unsigned c = 0; c < components; c++) - data.i[c] = ~ op[0]->value.i[c]; - break; - case GLSL_TYPE_UINT: - for (unsigned c = 0; c < components; c++) - data.u[c] = ~ op[0]->value.u[c]; - break; - default: - assert(0); - } - break; - - case ir_unop_logic_not: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = !op[0]->value.b[c]; - break; - - case ir_unop_f2i: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.i[c] = (int) op[0]->value.f[c]; - } - break; - case ir_unop_i2f: - assert(op[0]->type->base_type == GLSL_TYPE_INT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = (float) op[0]->value.i[c]; - } - break; - case ir_unop_u2f: - assert(op[0]->type->base_type == GLSL_TYPE_UINT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = (float) op[0]->value.u[c]; - } - break; - case ir_unop_b2f: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F; - } - break; - case ir_unop_f2b: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.b[c] = op[0]->value.f[c] != 0.0F ? true : false; - } - break; - case ir_unop_b2i: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.u[c] = op[0]->value.b[c] ? 1 : 0; - } - break; - case ir_unop_i2b: - assert(op[0]->type->is_integer()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.b[c] = op[0]->value.u[c] ? true : false; - } - break; - - case ir_unop_any: - assert(op[0]->type->is_boolean()); - data.b[0] = false; - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (op[0]->value.b[c]) - data.b[0] = true; - } - break; - - case ir_unop_trunc: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = truncf(op[0]->value.f[c]); - } - break; - - case ir_unop_ceil: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = ceilf(op[0]->value.f[c]); - } - break; - - case ir_unop_floor: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = floorf(op[0]->value.f[c]); - } - break; - - case ir_unop_fract: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = 0; - break; - case GLSL_TYPE_INT: - data.i[c] = 0; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]); - break; - default: - assert(0); - } - } - break; - - case ir_unop_sin: - case ir_unop_sin_reduced: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = sinf(op[0]->value.f[c]); - } - break; - - case ir_unop_cos: - case ir_unop_cos_reduced: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = cosf(op[0]->value.f[c]); - } - break; - - case ir_unop_neg: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = -((int) op[0]->value.u[c]); - break; - case GLSL_TYPE_INT: - data.i[c] = -op[0]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = -op[0]->value.f[c]; - break; - default: - assert(0); - } - } - break; - - case ir_unop_abs: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c]; - if (data.i[c] < 0) - data.i[c] = -data.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = fabs(op[0]->value.f[c]); - break; - default: - assert(0); - } - } - break; - - case ir_unop_sign: - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.i[c] > 0; - break; - case GLSL_TYPE_INT: - data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0)); - break; - default: - assert(0); - } - } - break; - - case ir_unop_rcp: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (this->type->base_type) { - case GLSL_TYPE_UINT: - if (op[0]->value.u[c] != 0.0) - data.u[c] = 1 / op[0]->value.u[c]; - break; - case GLSL_TYPE_INT: - if (op[0]->value.i[c] != 0.0) - data.i[c] = 1 / op[0]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - if (op[0]->value.f[c] != 0.0) - data.f[c] = 1.0F / op[0]->value.f[c]; - break; - default: - assert(0); - } - } - break; - - case ir_unop_rsq: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = 1.0F / sqrtf(op[0]->value.f[c]); - } - break; - - case ir_unop_sqrt: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = sqrtf(op[0]->value.f[c]); - } - break; - - case ir_unop_exp: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = expf(op[0]->value.f[c]); - } - break; - - case ir_unop_exp2: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = exp2f(op[0]->value.f[c]); - } - break; - - case ir_unop_log: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = logf(op[0]->value.f[c]); - } - break; - - case ir_unop_log2: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = log2f(op[0]->value.f[c]); - } - break; - - case ir_unop_dFdx: - case ir_unop_dFdy: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = 0.0; - } - break; - - case ir_binop_pow: - assert(op[0]->type->base_type == GLSL_TYPE_FLOAT); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]); - } - break; - - case ir_binop_dot: - data.f[0] = dot(op[0], op[1]); - break; - - case ir_binop_min: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]); - break; - case GLSL_TYPE_INT: - data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - - break; - case ir_binop_max: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]); - break; - case GLSL_TYPE_INT: - data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - break; - - case ir_binop_add: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_sub: - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_mul: - /* Check for equal types, or unequal types involving scalars */ - if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix()) - || op0_scalar || op1_scalar) { - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1]; - break; - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - } else { - assert(op[0]->type->is_matrix() || op[1]->type->is_matrix()); - - /* Multiply an N-by-M matrix with an M-by-P matrix. Since either - * matrix can be a GLSL vector, either N or P can be 1. - * - * For vec*mat, the vector is treated as a row vector. This - * means the vector is a 1-row x M-column matrix. - * - * For mat*vec, the vector is treated as a column vector. Since - * matrix_columns is 1 for vectors, this just works. - */ - const unsigned n = op[0]->type->is_vector() - ? 1 : op[0]->type->vector_elements; - const unsigned m = op[1]->type->vector_elements; - const unsigned p = op[1]->type->matrix_columns; - for (unsigned j = 0; j < p; j++) { - for (unsigned i = 0; i < n; i++) { - for (unsigned k = 0; k < m; k++) { - data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j]; - } - } - } - } - - break; - case ir_binop_div: - /* FINISHME: Emit warning when division-by-zero is detected. */ - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - if (op[1]->value.u[c1] == 0) { - data.u[c] = 0; - } else { - data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1]; - } - break; - case GLSL_TYPE_INT: - if (op[1]->value.i[c1] == 0) { - data.i[c] = 0; - } else { - data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1]; - } - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1]; - break; - default: - assert(0); - } - } - - break; - case ir_binop_mod: - /* FINISHME: Emit warning when division-by-zero is detected. */ - assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar); - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - if (op[1]->value.u[c1] == 0) { - data.u[c] = 0; - } else { - data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1]; - } - break; - case GLSL_TYPE_INT: - if (op[1]->value.i[c1] == 0) { - data.i[c] = 0; - } else { - data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1]; - } - break; - case GLSL_TYPE_FLOAT: - /* We don't use fmod because it rounds toward zero; GLSL specifies - * the use of floor. - */ - data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1] - * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]); - break; - default: - assert(0); - } - } - - break; - - case ir_binop_logic_and: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] && op[1]->value.b[c]; - break; - case ir_binop_logic_xor: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c]; - break; - case ir_binop_logic_or: - assert(op[0]->type->base_type == GLSL_TYPE_BOOL); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.b[c] = op[0]->value.b[c] || op[1]->value.b[c]; - break; - - case ir_binop_less: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] < op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] < op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] < op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_greater: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_lequal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_gequal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0]; - break; - case GLSL_TYPE_INT: - data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0]; - break; - case GLSL_TYPE_FLOAT: - data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0]; - break; - default: - assert(0); - } - } - break; - case ir_binop_equal: - assert(op[0]->type == op[1]->type); - for (unsigned c = 0; c < components; c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_nequal: - assert(op[0]->type != op[1]->type); - for (unsigned c = 0; c < components; c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; - break; - default: - assert(0); - } - } - break; - case ir_binop_all_equal: - data.b[0] = op[0]->has_value(op[1]); - break; - case ir_binop_any_nequal: - data.b[0] = !op[0]->has_value(op[1]); - break; - - case ir_binop_lshift: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1]; - } - } - break; - - case ir_binop_rshift: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_INT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_INT) { - data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1]; - - } else if (op[0]->type->base_type == GLSL_TYPE_UINT && - op[1]->type->base_type == GLSL_TYPE_UINT) { - data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1]; - } - } - break; - - case ir_binop_bit_and: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_binop_bit_or: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_binop_bit_xor: - for (unsigned c = 0, c0 = 0, c1 = 0; - c < components; - c0 += c0_inc, c1 += c1_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1]; - break; - default: - assert(0); - } - } - break; - - case ir_quadop_vector: - for (unsigned c = 0; c < this->type->vector_elements; c++) { - switch (this->type->base_type) { - case GLSL_TYPE_INT: - data.i[c] = op[c]->value.i[0]; - break; - case GLSL_TYPE_UINT: - data.u[c] = op[c]->value.u[0]; - break; - case GLSL_TYPE_FLOAT: - data.f[c] = op[c]->value.f[0]; - break; - default: - assert(0); - } - } - break; - - default: - /* FINISHME: Should handle all expression types. */ - return NULL; - } - - return new(ctx) ir_constant(this->type, &data); -} - - -ir_constant * -ir_texture::constant_expression_value() -{ - /* texture lookups aren't constant expressions */ - return NULL; -} - - -ir_constant * -ir_swizzle::constant_expression_value() -{ - ir_constant *v = this->val->constant_expression_value(); - - if (v != NULL) { - ir_constant_data data = { { 0 } }; - - const unsigned swiz_idx[4] = { - this->mask.x, this->mask.y, this->mask.z, this->mask.w - }; - - for (unsigned i = 0; i < this->mask.num_components; i++) { - switch (v->type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break; - case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break; - case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break; - default: assert(!"Should not get here."); break; - } - } - - void *ctx = ralloc_parent(this); - return new(ctx) ir_constant(this->type, &data); - } - return NULL; -} - - -ir_constant * -ir_dereference_variable::constant_expression_value() -{ - /* This may occur during compile and var->type is glsl_type::error_type */ - if (!var) - return NULL; - - /* The constant_value of a uniform variable is its initializer, - * not the lifetime constant value of the uniform. - */ - if (var->mode == ir_var_uniform) - return NULL; - - if (!var->constant_value) - return NULL; - - return var->constant_value->clone(ralloc_parent(var), NULL); -} - - -ir_constant * -ir_dereference_array::constant_expression_value() -{ - ir_constant *array = this->array->constant_expression_value(); - ir_constant *idx = this->array_index->constant_expression_value(); - - if ((array != NULL) && (idx != NULL)) { - void *ctx = ralloc_parent(this); - if (array->type->is_matrix()) { - /* Array access of a matrix results in a vector. - */ - const unsigned column = idx->value.u[0]; - - const glsl_type *const column_type = array->type->column_type(); - - /* Offset in the constant matrix to the first element of the column - * to be extracted. - */ - const unsigned mat_idx = column * column_type->vector_elements; - - ir_constant_data data = { { 0 } }; - - switch (column_type->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - for (unsigned i = 0; i < column_type->vector_elements; i++) - data.u[i] = array->value.u[mat_idx + i]; - - break; - - case GLSL_TYPE_FLOAT: - for (unsigned i = 0; i < column_type->vector_elements; i++) - data.f[i] = array->value.f[mat_idx + i]; - - break; - - default: - assert(!"Should not get here."); - break; - } - - return new(ctx) ir_constant(column_type, &data); - } else if (array->type->is_vector()) { - const unsigned component = idx->value.u[0]; - - return new(ctx) ir_constant(array, component); - } else { - const unsigned index = idx->value.u[0]; - return array->get_array_element(index)->clone(ctx, NULL); - } - } - return NULL; -} - - -ir_constant * -ir_dereference_record::constant_expression_value() -{ - ir_constant *v = this->record->constant_expression_value(); - - return (v != NULL) ? v->get_record_field(this->field) : NULL; -} - - -ir_constant * -ir_assignment::constant_expression_value() -{ - /* FINISHME: Handle CEs involving assignment (return RHS) */ - return NULL; -} - - -ir_constant * -ir_constant::constant_expression_value() -{ - return this; -} - - -ir_constant * -ir_call::constant_expression_value() -{ - if (this->type == glsl_type::error_type) - return NULL; - - /* From the GLSL 1.20 spec, page 23: - * "Function calls to user-defined functions (non-built-in functions) - * cannot be used to form constant expressions." - */ - if (!this->callee->is_builtin) - return NULL; - - unsigned num_parameters = 0; - - /* Check if all parameters are constant */ - ir_constant *op[3]; - foreach_list(n, &this->actual_parameters) { - ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value(); - if (constant == NULL) - return NULL; - - op[num_parameters] = constant; - - assert(num_parameters < 3); - num_parameters++; - } - - /* Individual cases below can either: - * - Assign "expr" a new ir_expression to evaluate (for basic opcodes) - * - Fill "data" with appopriate constant data - * - Return an ir_constant directly. - */ - void *mem_ctx = ralloc_parent(this); - ir_expression *expr = NULL; - - ir_constant_data data; - memset(&data, 0, sizeof(data)); - - const char *callee = this->callee_name(); - if (strcmp(callee, "abs") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL); - } else if (strcmp(callee, "all") == 0) { - assert(op[0]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (!op[0]->value.b[c]) - return new(mem_ctx) ir_constant(false); - } - return new(mem_ctx) ir_constant(true); - } else if (strcmp(callee, "any") == 0) { - assert(op[0]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - if (op[0]->value.b[c]) - return new(mem_ctx) ir_constant(true); - } - return new(mem_ctx) ir_constant(false); - } else if (strcmp(callee, "acos") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = acosf(op[0]->value.f[c]); - } else if (strcmp(callee, "acosh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = acoshf(op[0]->value.f[c]); - } else if (strcmp(callee, "asin") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = asinf(op[0]->value.f[c]); - } else if (strcmp(callee, "asinh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = asinhf(op[0]->value.f[c]); - } else if (strcmp(callee, "atan") == 0) { - assert(op[0]->type->is_float()); - if (num_parameters == 2) { - assert(op[1]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]); - } else { - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atanf(op[0]->value.f[c]); - } - } else if (strcmp(callee, "atanh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = atanhf(op[0]->value.f[c]); - } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) { - return ir_constant::zero(mem_ctx, this->type); - } else if (strcmp(callee, "ceil") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL); - } else if (strcmp(callee, "clamp") == 0) { - assert(num_parameters == 3); - unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1; - unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, c1 = 0, c2 = 0; - c < op[0]->type->components(); - c1 += c1_inc, c2 += c2_inc, c++) { - - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1], - op[2]->value.u[c2]); - break; - case GLSL_TYPE_INT: - data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1], - op[2]->value.i[c2]); - break; - case GLSL_TYPE_FLOAT: - data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1], - op[2]->value.f[c2]); - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "cos") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL); - } else if (strcmp(callee, "cosh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = coshf(op[0]->value.f[c]); - } else if (strcmp(callee, "cross") == 0) { - assert(op[0]->type == glsl_type::vec3_type); - assert(op[1]->type == glsl_type::vec3_type); - data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] - - op[1]->value.f[1] * op[0]->value.f[2]); - data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] - - op[1]->value.f[2] * op[0]->value.f[0]); - data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] - - op[1]->value.f[0] * op[0]->value.f[1]); - } else if (strcmp(callee, "degrees") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = 180.0F / M_PI * op[0]->value.f[c]; - } else if (strcmp(callee, "distance") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - float length_squared = 0.0; - for (unsigned c = 0; c < op[0]->type->components(); c++) { - float t = op[0]->value.f[c] - op[1]->value.f[c]; - length_squared += t * t; - } - return new(mem_ctx) ir_constant(sqrtf(length_squared)); - } else if (strcmp(callee, "dot") == 0) { - return new(mem_ctx) ir_constant(dot(op[0], op[1])); - } else if (strcmp(callee, "equal") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] == op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] == op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] == op[1]->value.f[c]; - break; - case GLSL_TYPE_BOOL: - data.b[c] = op[0]->value.b[c] == op[1]->value.b[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "exp") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL); - } else if (strcmp(callee, "exp2") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL); - } else if (strcmp(callee, "faceforward") == 0) { - if (dot(op[2], op[1]) < 0) - return op[0]; - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = -op[0]->value.f[c]; - } else if (strcmp(callee, "floor") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL); - } else if (strcmp(callee, "fract") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL); - } else if (strcmp(callee, "fwidth") == 0) { - return ir_constant::zero(mem_ctx, this->type); - } else if (strcmp(callee, "greaterThan") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] > op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] > op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] > op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "greaterThanEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "inversesqrt") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL); - } else if (strcmp(callee, "length") == 0) { - return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0]))); - } else if (strcmp(callee, "lessThan") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] < op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] < op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] < op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "lessThanEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "log") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL); - } else if (strcmp(callee, "log2") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL); - } else if (strcmp(callee, "matrixCompMult") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] * op[1]->value.f[c]; - } else if (strcmp(callee, "max") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]); - } else if (strcmp(callee, "min") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]); - } else if (strcmp(callee, "mix") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - if (op[2]->type->is_float()) { - unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1; - unsigned components = op[0]->type->components(); - for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) { - data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) + - op[1]->value.f[c] * op[2]->value.f[c2]; - } - } else { - assert(op[2]->type->is_boolean()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c]; - } - } else if (strcmp(callee, "mod") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]); - } else if (strcmp(callee, "normalize") == 0) { - assert(op[0]->type->is_float()); - float length = sqrtf(dot(op[0], op[0])); - - if (length == 0) - return ir_constant::zero(mem_ctx, this->type); - - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] / length; - } else if (strcmp(callee, "not") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL); - } else if (strcmp(callee, "notEqual") == 0) { - assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector()); - for (unsigned c = 0; c < op[0]->type->components(); c++) { - switch (op[0]->type->base_type) { - case GLSL_TYPE_UINT: - data.b[c] = op[0]->value.u[c] != op[1]->value.u[c]; - break; - case GLSL_TYPE_INT: - data.b[c] = op[0]->value.i[c] != op[1]->value.i[c]; - break; - case GLSL_TYPE_FLOAT: - data.b[c] = op[0]->value.f[c] != op[1]->value.f[c]; - break; - case GLSL_TYPE_BOOL: - data.b[c] = op[0]->value.b[c] != op[1]->value.b[c]; - break; - default: - assert(!"Should not get here."); - } - } - } else if (strcmp(callee, "outerProduct") == 0) { - assert(op[0]->type->is_vector() && op[1]->type->is_vector()); - const unsigned m = op[0]->type->vector_elements; - const unsigned n = op[1]->type->vector_elements; - for (unsigned j = 0; j < n; j++) { - for (unsigned i = 0; i < m; i++) { - data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j]; - } - } - } else if (strcmp(callee, "pow") == 0) { - expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]); - } else if (strcmp(callee, "radians") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = M_PI / 180.0F * op[0]->value.f[c]; - } else if (strcmp(callee, "reflect") == 0) { - assert(op[0]->type->is_float()); - float dot_NI = dot(op[1], op[0]); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c]; - } else if (strcmp(callee, "refract") == 0) { - const float eta = op[2]->value.f[0]; - const float dot_NI = dot(op[1], op[0]); - const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI); - if (k < 0.0) { - return ir_constant::zero(mem_ctx, this->type); - } else { - for (unsigned c = 0; c < type->components(); c++) { - data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k)) - * op[1]->value.f[c]; - } - } - } else if (strcmp(callee, "sign") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL); - } else if (strcmp(callee, "sin") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL); - } else if (strcmp(callee, "sinh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = sinhf(op[0]->value.f[c]); - } else if (strcmp(callee, "smoothstep") == 0) { - assert(num_parameters == 3); - assert(op[1]->type == op[0]->type); - unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) { - const float edge0 = op[0]->value.f[e]; - const float edge1 = op[1]->value.f[e]; - if (edge0 == edge1) { - data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */ - } else { - const float numerator = op[2]->value.f[c] - edge0; - const float denominator = edge1 - edge0; - const float t = CLAMP(numerator/denominator, 0, 1); - data.f[c] = t * t * (3 - 2 * t); - } - } - } else if (strcmp(callee, "sqrt") == 0) { - expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL); - } else if (strcmp(callee, "step") == 0) { - assert(op[0]->type->is_float() && op[1]->type->is_float()); - /* op[0] (edge) may be either a scalar or a vector */ - const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1; - for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++) - data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F; - } else if (strcmp(callee, "tan") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = tanf(op[0]->value.f[c]); - } else if (strcmp(callee, "tanh") == 0) { - assert(op[0]->type->is_float()); - for (unsigned c = 0; c < op[0]->type->components(); c++) - data.f[c] = tanhf(op[0]->value.f[c]); - } else if (strcmp(callee, "transpose") == 0) { - assert(op[0]->type->is_matrix()); - const unsigned n = op[0]->type->vector_elements; - const unsigned m = op[0]->type->matrix_columns; - for (unsigned j = 0; j < m; j++) { - for (unsigned i = 0; i < n; i++) { - data.f[m*i+j] += op[0]->value.f[i+n*j]; - } - } - } else { - /* Unsupported builtin - some are not allowed in constant expressions. */ - return NULL; - } - - if (expr != NULL) - return expr->constant_expression_value(); - - return new(mem_ctx) ir_constant(this->type, &data); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ir_constant_expression.cpp
+ * Evaluate and process constant valued expressions
+ *
+ * In GLSL, constant valued expressions are used in several places. These
+ * must be processed and evaluated very early in the compilation process.
+ *
+ * * Sizes of arrays
+ * * Initializers for uniforms
+ * * Initializers for \c const variables
+ */
+
+#include <math.h>
+#include "main/core.h" /* for MAX2, MIN2, CLAMP */
+#include "ir.h"
+#include "ir_visitor.h"
+#include "glsl_types.h"
+
+static float
+dot(ir_constant *op0, ir_constant *op1)
+{
+ assert(op0->type->is_float() && op1->type->is_float());
+
+ float result = 0;
+ for (unsigned c = 0; c < op0->type->components(); c++)
+ result += op0->value.f[c] * op1->value.f[c];
+
+ return result;
+}
+
+ir_constant *
+ir_expression::constant_expression_value()
+{
+ if (this->type->is_error())
+ return NULL;
+
+ ir_constant *op[Elements(this->operands)] = { NULL, };
+ ir_constant_data data;
+
+ memset(&data, 0, sizeof(data));
+
+ for (unsigned operand = 0; operand < this->get_num_operands(); operand++) {
+ op[operand] = this->operands[operand]->constant_expression_value();
+ if (!op[operand])
+ return NULL;
+ }
+
+ if (op[1] != NULL)
+ assert(op[0]->type->base_type == op[1]->type->base_type);
+
+ bool op0_scalar = op[0]->type->is_scalar();
+ bool op1_scalar = op[1] != NULL && op[1]->type->is_scalar();
+
+ /* When iterating over a vector or matrix's components, we want to increase
+ * the loop counter. However, for scalars, we want to stay at 0.
+ */
+ unsigned c0_inc = op0_scalar ? 0 : 1;
+ unsigned c1_inc = op1_scalar ? 0 : 1;
+ unsigned components;
+ if (op1_scalar || !op[1]) {
+ components = op[0]->type->components();
+ } else {
+ components = op[1]->type->components();
+ }
+
+ void *ctx = ralloc_parent(this);
+
+ /* Handle array operations here, rather than below. */
+ if (op[0]->type->is_array()) {
+ assert(op[1] != NULL && op[1]->type->is_array());
+ switch (this->operation) {
+ case ir_binop_all_equal:
+ return new(ctx) ir_constant(op[0]->has_value(op[1]));
+ case ir_binop_any_nequal:
+ return new(ctx) ir_constant(!op[0]->has_value(op[1]));
+ default:
+ break;
+ }
+ return NULL;
+ }
+
+ switch (this->operation) {
+ case ir_unop_bit_not:
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ for (unsigned c = 0; c < components; c++)
+ data.i[c] = ~ op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_UINT:
+ for (unsigned c = 0; c < components; c++)
+ data.u[c] = ~ op[0]->value.u[c];
+ break;
+ default:
+ assert(0);
+ }
+ break;
+
+ case ir_unop_logic_not:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = !op[0]->value.b[c];
+ break;
+
+ case ir_unop_f2i:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.i[c] = (int) op[0]->value.f[c];
+ }
+ break;
+ case ir_unop_i2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_INT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = (float) op[0]->value.i[c];
+ }
+ break;
+ case ir_unop_u2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_UINT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = (float) op[0]->value.u[c];
+ }
+ break;
+ case ir_unop_b2f:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = op[0]->value.b[c] ? 1.0F : 0.0F;
+ }
+ break;
+ case ir_unop_f2b:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.b[c] = op[0]->value.f[c] != 0.0F ? true : false;
+ }
+ break;
+ case ir_unop_b2i:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.u[c] = op[0]->value.b[c] ? 1 : 0;
+ }
+ break;
+ case ir_unop_i2b:
+ assert(op[0]->type->is_integer());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.b[c] = op[0]->value.u[c] ? true : false;
+ }
+ break;
+
+ case ir_unop_any:
+ assert(op[0]->type->is_boolean());
+ data.b[0] = false;
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (op[0]->value.b[c])
+ data.b[0] = true;
+ }
+ break;
+
+ case ir_unop_trunc:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = truncf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_ceil:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = ceilf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_floor:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = floorf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_fract:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = 0;
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = 0;
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c] - floor(op[0]->value.f[c]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_sin:
+ case ir_unop_sin_reduced:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = sinf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_cos:
+ case ir_unop_cos_reduced:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = cosf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_neg:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = -((int) op[0]->value.u[c]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = -op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = -op[0]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_abs:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c];
+ if (data.i[c] < 0)
+ data.i[c] = -data.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = fabs(op[0]->value.f[c]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_sign:
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.i[c] > 0;
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = (op[0]->value.i[c] > 0) - (op[0]->value.i[c] < 0);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = float((op[0]->value.f[c] > 0)-(op[0]->value.f[c] < 0));
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_rcp:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[0]->value.u[c] != 0.0)
+ data.u[c] = 1 / op[0]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ if (op[0]->value.i[c] != 0.0)
+ data.i[c] = 1 / op[0]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ if (op[0]->value.f[c] != 0.0)
+ data.f[c] = 1.0F / op[0]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_unop_rsq:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = 1.0F / sqrtf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_sqrt:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = sqrtf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_exp:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = expf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_exp2:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = exp2f(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_log:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = logf(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_log2:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = log2f(op[0]->value.f[c]);
+ }
+ break;
+
+ case ir_unop_dFdx:
+ case ir_unop_dFdy:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = 0.0;
+ }
+ break;
+
+ case ir_binop_pow:
+ assert(op[0]->type->base_type == GLSL_TYPE_FLOAT);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ data.f[c] = powf(op[0]->value.f[c], op[1]->value.f[c]);
+ }
+ break;
+
+ case ir_binop_dot:
+ data.f[0] = dot(op[0], op[1]);
+ break;
+
+ case ir_binop_min:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = MIN2(op[0]->value.u[c0], op[1]->value.u[c1]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = MIN2(op[0]->value.i[c0], op[1]->value.i[c1]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = MIN2(op[0]->value.f[c0], op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_max:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = MAX2(op[0]->value.u[c0], op[1]->value.u[c1]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = MAX2(op[0]->value.i[c0], op[1]->value.i[c1]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = MAX2(op[0]->value.f[c0], op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_add:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] + op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] + op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] + op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_sub:
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] - op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] - op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_mul:
+ /* Check for equal types, or unequal types involving scalars */
+ if ((op[0]->type == op[1]->type && !op[0]->type->is_matrix())
+ || op0_scalar || op1_scalar) {
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] * op[1]->value.u[c1];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] * op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] * op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ } else {
+ assert(op[0]->type->is_matrix() || op[1]->type->is_matrix());
+
+ /* Multiply an N-by-M matrix with an M-by-P matrix. Since either
+ * matrix can be a GLSL vector, either N or P can be 1.
+ *
+ * For vec*mat, the vector is treated as a row vector. This
+ * means the vector is a 1-row x M-column matrix.
+ *
+ * For mat*vec, the vector is treated as a column vector. Since
+ * matrix_columns is 1 for vectors, this just works.
+ */
+ const unsigned n = op[0]->type->is_vector()
+ ? 1 : op[0]->type->vector_elements;
+ const unsigned m = op[1]->type->vector_elements;
+ const unsigned p = op[1]->type->matrix_columns;
+ for (unsigned j = 0; j < p; j++) {
+ for (unsigned i = 0; i < n; i++) {
+ for (unsigned k = 0; k < m; k++) {
+ data.f[i+n*j] += op[0]->value.f[i+n*k]*op[1]->value.f[k+m*j];
+ }
+ }
+ }
+ }
+
+ break;
+ case ir_binop_div:
+ /* FINISHME: Emit warning when division-by-zero is detected. */
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[1]->value.u[c1] == 0) {
+ data.u[c] = 0;
+ } else {
+ data.u[c] = op[0]->value.u[c0] / op[1]->value.u[c1];
+ }
+ break;
+ case GLSL_TYPE_INT:
+ if (op[1]->value.i[c1] == 0) {
+ data.i[c] = 0;
+ } else {
+ data.i[c] = op[0]->value.i[c0] / op[1]->value.i[c1];
+ }
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[0]->value.f[c0] / op[1]->value.f[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+ case ir_binop_mod:
+ /* FINISHME: Emit warning when division-by-zero is detected. */
+ assert(op[0]->type == op[1]->type || op0_scalar || op1_scalar);
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ if (op[1]->value.u[c1] == 0) {
+ data.u[c] = 0;
+ } else {
+ data.u[c] = op[0]->value.u[c0] % op[1]->value.u[c1];
+ }
+ break;
+ case GLSL_TYPE_INT:
+ if (op[1]->value.i[c1] == 0) {
+ data.i[c] = 0;
+ } else {
+ data.i[c] = op[0]->value.i[c0] % op[1]->value.i[c1];
+ }
+ break;
+ case GLSL_TYPE_FLOAT:
+ /* We don't use fmod because it rounds toward zero; GLSL specifies
+ * the use of floor.
+ */
+ data.f[c] = op[0]->value.f[c0] - op[1]->value.f[c1]
+ * floorf(op[0]->value.f[c0] / op[1]->value.f[c1]);
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ break;
+
+ case ir_binop_logic_and:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] && op[1]->value.b[c];
+ break;
+ case ir_binop_logic_xor:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] ^ op[1]->value.b[c];
+ break;
+ case ir_binop_logic_or:
+ assert(op[0]->type->base_type == GLSL_TYPE_BOOL);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.b[c] = op[0]->value.b[c] || op[1]->value.b[c];
+ break;
+
+ case ir_binop_less:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] < op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] < op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] < op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_greater:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_lequal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] <= op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] <= op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] <= op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_gequal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[0] = op[0]->value.u[0] >= op[1]->value.u[0];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[0] = op[0]->value.i[0] >= op[1]->value.i[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[0] = op[0]->value.f[0] >= op[1]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_equal:
+ assert(op[0]->type == op[1]->type);
+ for (unsigned c = 0; c < components; c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_nequal:
+ assert(op[0]->type != op[1]->type);
+ for (unsigned c = 0; c < components; c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+ case ir_binop_all_equal:
+ data.b[0] = op[0]->has_value(op[1]);
+ break;
+ case ir_binop_any_nequal:
+ data.b[0] = !op[0]->has_value(op[1]);
+ break;
+
+ case ir_binop_lshift:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.i[c] = op[0]->value.i[c0] << op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.i[c] = op[0]->value.i[c0] << op[1]->value.u[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.u[c] = op[0]->value.u[c0] << op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.u[c] = op[0]->value.u[c0] << op[1]->value.u[c1];
+ }
+ }
+ break;
+
+ case ir_binop_rshift:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.i[c] = op[0]->value.i[c0] >> op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_INT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.i[c] = op[0]->value.i[c0] >> op[1]->value.u[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_INT) {
+ data.u[c] = op[0]->value.u[c0] >> op[1]->value.i[c1];
+
+ } else if (op[0]->type->base_type == GLSL_TYPE_UINT &&
+ op[1]->type->base_type == GLSL_TYPE_UINT) {
+ data.u[c] = op[0]->value.u[c0] >> op[1]->value.u[c1];
+ }
+ }
+ break;
+
+ case ir_binop_bit_and:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] & op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] & op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_bit_or:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] | op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] | op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_binop_bit_xor:
+ for (unsigned c = 0, c0 = 0, c1 = 0;
+ c < components;
+ c0 += c0_inc, c1 += c1_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[0]->value.i[c0] ^ op[1]->value.i[c1];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[0]->value.u[c0] ^ op[1]->value.u[c1];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ case ir_quadop_vector:
+ for (unsigned c = 0; c < this->type->vector_elements; c++) {
+ switch (this->type->base_type) {
+ case GLSL_TYPE_INT:
+ data.i[c] = op[c]->value.i[0];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[c] = op[c]->value.u[0];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = op[c]->value.f[0];
+ break;
+ default:
+ assert(0);
+ }
+ }
+ break;
+
+ default:
+ /* FINISHME: Should handle all expression types. */
+ return NULL;
+ }
+
+ return new(ctx) ir_constant(this->type, &data);
+}
+
+
+ir_constant *
+ir_texture::constant_expression_value()
+{
+ /* texture lookups aren't constant expressions */
+ return NULL;
+}
+
+
+ir_constant *
+ir_swizzle::constant_expression_value()
+{
+ ir_constant *v = this->val->constant_expression_value();
+
+ if (v != NULL) {
+ ir_constant_data data = { { 0 } };
+
+ const unsigned swiz_idx[4] = {
+ this->mask.x, this->mask.y, this->mask.z, this->mask.w
+ };
+
+ for (unsigned i = 0; i < this->mask.num_components; i++) {
+ switch (v->type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT: data.u[i] = v->value.u[swiz_idx[i]]; break;
+ case GLSL_TYPE_FLOAT: data.f[i] = v->value.f[swiz_idx[i]]; break;
+ case GLSL_TYPE_BOOL: data.b[i] = v->value.b[swiz_idx[i]]; break;
+ default: assert(!"Should not get here."); break;
+ }
+ }
+
+ void *ctx = ralloc_parent(this);
+ return new(ctx) ir_constant(this->type, &data);
+ }
+ return NULL;
+}
+
+
+ir_constant *
+ir_dereference_variable::constant_expression_value()
+{
+ /* This may occur during compile and var->type is glsl_type::error_type */
+ if (!var)
+ return NULL;
+
+ /* The constant_value of a uniform variable is its initializer,
+ * not the lifetime constant value of the uniform.
+ */
+ if (var->mode == ir_var_uniform)
+ return NULL;
+
+ if (!var->constant_value)
+ return NULL;
+
+ return var->constant_value->clone(ralloc_parent(var), NULL);
+}
+
+
+ir_constant *
+ir_dereference_array::constant_expression_value()
+{
+ ir_constant *array = this->array->constant_expression_value();
+ ir_constant *idx = this->array_index->constant_expression_value();
+
+ if ((array != NULL) && (idx != NULL)) {
+ void *ctx = ralloc_parent(this);
+ if (array->type->is_matrix()) {
+ /* Array access of a matrix results in a vector.
+ */
+ const unsigned column = idx->value.u[0];
+
+ const glsl_type *const column_type = array->type->column_type();
+
+ /* Offset in the constant matrix to the first element of the column
+ * to be extracted.
+ */
+ const unsigned mat_idx = column * column_type->vector_elements;
+
+ ir_constant_data data = { { 0 } };
+
+ switch (column_type->base_type) {
+ case GLSL_TYPE_UINT:
+ case GLSL_TYPE_INT:
+ for (unsigned i = 0; i < column_type->vector_elements; i++)
+ data.u[i] = array->value.u[mat_idx + i];
+
+ break;
+
+ case GLSL_TYPE_FLOAT:
+ for (unsigned i = 0; i < column_type->vector_elements; i++)
+ data.f[i] = array->value.f[mat_idx + i];
+
+ break;
+
+ default:
+ assert(!"Should not get here.");
+ break;
+ }
+
+ return new(ctx) ir_constant(column_type, &data);
+ } else if (array->type->is_vector()) {
+ const unsigned component = idx->value.u[0];
+
+ return new(ctx) ir_constant(array, component);
+ } else {
+ const unsigned index = idx->value.u[0];
+ return array->get_array_element(index)->clone(ctx, NULL);
+ }
+ }
+ return NULL;
+}
+
+
+ir_constant *
+ir_dereference_record::constant_expression_value()
+{
+ ir_constant *v = this->record->constant_expression_value();
+
+ return (v != NULL) ? v->get_record_field(this->field) : NULL;
+}
+
+
+ir_constant *
+ir_assignment::constant_expression_value()
+{
+ /* FINISHME: Handle CEs involving assignment (return RHS) */
+ return NULL;
+}
+
+
+ir_constant *
+ir_constant::constant_expression_value()
+{
+ return this;
+}
+
+
+ir_constant *
+ir_call::constant_expression_value()
+{
+ if (this->type == glsl_type::error_type)
+ return NULL;
+
+ /* From the GLSL 1.20 spec, page 23:
+ * "Function calls to user-defined functions (non-built-in functions)
+ * cannot be used to form constant expressions."
+ */
+ if (!this->callee->is_builtin)
+ return NULL;
+
+ unsigned num_parameters = 0;
+
+ /* Check if all parameters are constant */
+ ir_constant *op[3];
+ foreach_list(n, &this->actual_parameters) {
+ ir_constant *constant = ((ir_rvalue *) n)->constant_expression_value();
+ if (constant == NULL)
+ return NULL;
+
+ op[num_parameters] = constant;
+
+ assert(num_parameters < 3);
+ num_parameters++;
+ }
+
+ /* Individual cases below can either:
+ * - Assign "expr" a new ir_expression to evaluate (for basic opcodes)
+ * - Fill "data" with appopriate constant data
+ * - Return an ir_constant directly.
+ */
+ void *mem_ctx = ralloc_parent(this);
+ ir_expression *expr = NULL;
+
+ ir_constant_data data;
+ memset(&data, 0, sizeof(data));
+
+ const char *callee = this->callee_name();
+ if (strcmp(callee, "abs") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_abs, type, op[0], NULL);
+ } else if (strcmp(callee, "all") == 0) {
+ assert(op[0]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (!op[0]->value.b[c])
+ return new(mem_ctx) ir_constant(false);
+ }
+ return new(mem_ctx) ir_constant(true);
+ } else if (strcmp(callee, "any") == 0) {
+ assert(op[0]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ if (op[0]->value.b[c])
+ return new(mem_ctx) ir_constant(true);
+ }
+ return new(mem_ctx) ir_constant(false);
+ } else if (strcmp(callee, "acos") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = acosf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "acosh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = acoshf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "asin") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = asinf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "asinh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = asinhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "atan") == 0) {
+ assert(op[0]->type->is_float());
+ if (num_parameters == 2) {
+ assert(op[1]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atan2f(op[0]->value.f[c], op[1]->value.f[c]);
+ } else {
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atanf(op[0]->value.f[c]);
+ }
+ } else if (strcmp(callee, "atanh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = atanhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "dFdx") == 0 || strcmp(callee, "dFdy") == 0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else if (strcmp(callee, "ceil") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_ceil, type, op[0], NULL);
+ } else if (strcmp(callee, "clamp") == 0) {
+ assert(num_parameters == 3);
+ unsigned c1_inc = op[1]->type->is_scalar() ? 0 : 1;
+ unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, c1 = 0, c2 = 0;
+ c < op[0]->type->components();
+ c1 += c1_inc, c2 += c2_inc, c++) {
+
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.u[c] = CLAMP(op[0]->value.u[c], op[1]->value.u[c1],
+ op[2]->value.u[c2]);
+ break;
+ case GLSL_TYPE_INT:
+ data.i[c] = CLAMP(op[0]->value.i[c], op[1]->value.i[c1],
+ op[2]->value.i[c2]);
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.f[c] = CLAMP(op[0]->value.f[c], op[1]->value.f[c1],
+ op[2]->value.f[c2]);
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "cos") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_cos, type, op[0], NULL);
+ } else if (strcmp(callee, "cosh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = coshf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "cross") == 0) {
+ assert(op[0]->type == glsl_type::vec3_type);
+ assert(op[1]->type == glsl_type::vec3_type);
+ data.f[0] = (op[0]->value.f[1] * op[1]->value.f[2] -
+ op[1]->value.f[1] * op[0]->value.f[2]);
+ data.f[1] = (op[0]->value.f[2] * op[1]->value.f[0] -
+ op[1]->value.f[2] * op[0]->value.f[0]);
+ data.f[2] = (op[0]->value.f[0] * op[1]->value.f[1] -
+ op[1]->value.f[0] * op[0]->value.f[1]);
+ } else if (strcmp(callee, "degrees") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = 180.0F / M_PI * op[0]->value.f[c];
+ } else if (strcmp(callee, "distance") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ float length_squared = 0.0;
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ float t = op[0]->value.f[c] - op[1]->value.f[c];
+ length_squared += t * t;
+ }
+ return new(mem_ctx) ir_constant(sqrtf(length_squared));
+ } else if (strcmp(callee, "dot") == 0) {
+ return new(mem_ctx) ir_constant(dot(op[0], op[1]));
+ } else if (strcmp(callee, "equal") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] == op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] == op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] == op[1]->value.f[c];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[c] = op[0]->value.b[c] == op[1]->value.b[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "exp") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_exp, type, op[0], NULL);
+ } else if (strcmp(callee, "exp2") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_exp2, type, op[0], NULL);
+ } else if (strcmp(callee, "faceforward") == 0) {
+ if (dot(op[2], op[1]) < 0)
+ return op[0];
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = -op[0]->value.f[c];
+ } else if (strcmp(callee, "floor") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_floor, type, op[0], NULL);
+ } else if (strcmp(callee, "fract") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_fract, type, op[0], NULL);
+ } else if (strcmp(callee, "fwidth") == 0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else if (strcmp(callee, "greaterThan") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] > op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] > op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] > op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "greaterThanEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] >= op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] >= op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] >= op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "inversesqrt") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_rsq, type, op[0], NULL);
+ } else if (strcmp(callee, "length") == 0) {
+ return new(mem_ctx) ir_constant(sqrtf(dot(op[0], op[0])));
+ } else if (strcmp(callee, "lessThan") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] < op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] < op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] < op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "lessThanEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] <= op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] <= op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] <= op[1]->value.f[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "log") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_log, type, op[0], NULL);
+ } else if (strcmp(callee, "log2") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_log2, type, op[0], NULL);
+ } else if (strcmp(callee, "matrixCompMult") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] * op[1]->value.f[c];
+ } else if (strcmp(callee, "max") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_max, type, op[0], op[1]);
+ } else if (strcmp(callee, "min") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_min, type, op[0], op[1]);
+ } else if (strcmp(callee, "mix") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ if (op[2]->type->is_float()) {
+ unsigned c2_inc = op[2]->type->is_scalar() ? 0 : 1;
+ unsigned components = op[0]->type->components();
+ for (unsigned c = 0, c2 = 0; c < components; c2 += c2_inc, c++) {
+ data.f[c] = op[0]->value.f[c] * (1 - op[2]->value.f[c2]) +
+ op[1]->value.f[c] * op[2]->value.f[c2];
+ }
+ } else {
+ assert(op[2]->type->is_boolean());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[op[2]->value.b[c] ? 1 : 0]->value.f[c];
+ }
+ } else if (strcmp(callee, "mod") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_mod, type, op[0], op[1]);
+ } else if (strcmp(callee, "normalize") == 0) {
+ assert(op[0]->type->is_float());
+ float length = sqrtf(dot(op[0], op[0]));
+
+ if (length == 0)
+ return ir_constant::zero(mem_ctx, this->type);
+
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] / length;
+ } else if (strcmp(callee, "not") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_logic_not, type, op[0], NULL);
+ } else if (strcmp(callee, "notEqual") == 0) {
+ assert(op[0]->type->is_vector() && op[1] && op[1]->type->is_vector());
+ for (unsigned c = 0; c < op[0]->type->components(); c++) {
+ switch (op[0]->type->base_type) {
+ case GLSL_TYPE_UINT:
+ data.b[c] = op[0]->value.u[c] != op[1]->value.u[c];
+ break;
+ case GLSL_TYPE_INT:
+ data.b[c] = op[0]->value.i[c] != op[1]->value.i[c];
+ break;
+ case GLSL_TYPE_FLOAT:
+ data.b[c] = op[0]->value.f[c] != op[1]->value.f[c];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[c] = op[0]->value.b[c] != op[1]->value.b[c];
+ break;
+ default:
+ assert(!"Should not get here.");
+ }
+ }
+ } else if (strcmp(callee, "outerProduct") == 0) {
+ assert(op[0]->type->is_vector() && op[1]->type->is_vector());
+ const unsigned m = op[0]->type->vector_elements;
+ const unsigned n = op[1]->type->vector_elements;
+ for (unsigned j = 0; j < n; j++) {
+ for (unsigned i = 0; i < m; i++) {
+ data.f[i+m*j] = op[0]->value.f[i] * op[1]->value.f[j];
+ }
+ }
+ } else if (strcmp(callee, "pow") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_binop_pow, type, op[0], op[1]);
+ } else if (strcmp(callee, "radians") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = M_PI / 180.0F * op[0]->value.f[c];
+ } else if (strcmp(callee, "reflect") == 0) {
+ assert(op[0]->type->is_float());
+ float dot_NI = dot(op[1], op[0]);
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = op[0]->value.f[c] - 2 * dot_NI * op[1]->value.f[c];
+ } else if (strcmp(callee, "refract") == 0) {
+ const float eta = op[2]->value.f[0];
+ const float dot_NI = dot(op[1], op[0]);
+ const float k = 1.0F - eta * eta * (1.0F - dot_NI * dot_NI);
+ if (k < 0.0) {
+ return ir_constant::zero(mem_ctx, this->type);
+ } else {
+ for (unsigned c = 0; c < type->components(); c++) {
+ data.f[c] = eta * op[0]->value.f[c] - (eta * dot_NI + sqrtf(k))
+ * op[1]->value.f[c];
+ }
+ }
+ } else if (strcmp(callee, "sign") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sign, type, op[0], NULL);
+ } else if (strcmp(callee, "sin") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sin, type, op[0], NULL);
+ } else if (strcmp(callee, "sinh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = sinhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "smoothstep") == 0) {
+ assert(num_parameters == 3);
+ assert(op[1]->type == op[0]->type);
+ unsigned edge_inc = op[0]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, e = 0; c < type->components(); e += edge_inc, c++) {
+ const float edge0 = op[0]->value.f[e];
+ const float edge1 = op[1]->value.f[e];
+ if (edge0 == edge1) {
+ data.f[c] = 0.0; /* Avoid a crash - results are undefined anyway */
+ } else {
+ const float numerator = op[2]->value.f[c] - edge0;
+ const float denominator = edge1 - edge0;
+ const float t = CLAMP(numerator/denominator, 0, 1);
+ data.f[c] = t * t * (3 - 2 * t);
+ }
+ }
+ } else if (strcmp(callee, "sqrt") == 0) {
+ expr = new(mem_ctx) ir_expression(ir_unop_sqrt, type, op[0], NULL);
+ } else if (strcmp(callee, "step") == 0) {
+ assert(op[0]->type->is_float() && op[1]->type->is_float());
+ /* op[0] (edge) may be either a scalar or a vector */
+ const unsigned c0_inc = op[0]->type->is_scalar() ? 0 : 1;
+ for (unsigned c = 0, c0 = 0; c < type->components(); c0 += c0_inc, c++)
+ data.f[c] = (op[1]->value.f[c] < op[0]->value.f[c0]) ? 0.0F : 1.0F;
+ } else if (strcmp(callee, "tan") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = tanf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "tanh") == 0) {
+ assert(op[0]->type->is_float());
+ for (unsigned c = 0; c < op[0]->type->components(); c++)
+ data.f[c] = tanhf(op[0]->value.f[c]);
+ } else if (strcmp(callee, "transpose") == 0) {
+ assert(op[0]->type->is_matrix());
+ const unsigned n = op[0]->type->vector_elements;
+ const unsigned m = op[0]->type->matrix_columns;
+ for (unsigned j = 0; j < m; j++) {
+ for (unsigned i = 0; i < n; i++) {
+ data.f[m*i+j] += op[0]->value.f[i+n*j];
+ }
+ }
+ } else {
+ /* Unsupported builtin - some are not allowed in constant expressions. */
+ return NULL;
+ }
+
+ if (expr != NULL)
+ return expr->constant_expression_value();
+
+ return new(mem_ctx) ir_constant(this->type, &data);
+}
diff --git a/mesalib/src/glsl/ir_import_prototypes.cpp b/mesalib/src/glsl/ir_import_prototypes.cpp index 3585bf6b2..03d40bb68 100644 --- a/mesalib/src/glsl/ir_import_prototypes.cpp +++ b/mesalib/src/glsl/ir_import_prototypes.cpp @@ -1,122 +1,122 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file ir_import_prototypes.cpp - * Import function prototypes from one IR tree into another. - * - * \author Ian Romanick - */ -#include "ir.h" -#include "glsl_symbol_table.h" - -/** - * Visitor used to import function prototypes - * - * Normally the \c clone method of either \c ir_function or - * \c ir_function_signature could be used. However, we don't want a complete - * clone of the \c ir_function_signature. We want everything \b except the - * body of the function. - */ -class import_prototype_visitor : public ir_hierarchical_visitor { -public: - /** - */ - import_prototype_visitor(exec_list *list, glsl_symbol_table *symbols, - void *mem_ctx) - { - this->mem_ctx = mem_ctx; - this->list = list; - this->symbols = symbols; - this->function = NULL; - } - - virtual ir_visitor_status visit_enter(ir_function *ir) - { - assert(this->function == NULL); - - this->function = this->symbols->get_function(ir->name); - if (!this->function) { - this->function = new(this->mem_ctx) ir_function(ir->name); - - list->push_tail(this->function); - - /* Add the new function to the symbol table. - */ - this->symbols->add_function(this->function); - } - return visit_continue; - } - - virtual ir_visitor_status visit_leave(ir_function *ir) - { - (void) ir; - assert(this->function != NULL); - - this->function = NULL; - return visit_continue; - } - - ir_visitor_status visit_enter(ir_function_signature *ir) - { - assert(this->function != NULL); - - ir_function_signature *copy = ir->clone_prototype(mem_ctx, NULL); - - this->function->add_signature(copy); - - /* Do not process child nodes of the ir_function_signature. There can - * never be any nodes inside the ir_function_signature that we care - * about. Instead continue with the next sibling. - */ - return visit_continue_with_parent; - } - -private: - exec_list *list; - ir_function *function; - glsl_symbol_table *symbols; - void *mem_ctx; -}; - - -/** - * Import function prototypes from one IR tree into another - * - * \param source Source instruction stream containing functions whose - * prototypes are to be imported - * \param dest Destination instruction stream where new \c ir_function and - * \c ir_function_signature nodes will be stored - * \param symbols Symbol table where new functions will be stored - * \param mem_ctx ralloc memory context used for new allocations - */ -void -import_prototypes(const exec_list *source, exec_list *dest, - glsl_symbol_table *symbols, void *mem_ctx) -{ - import_prototype_visitor v(dest, symbols, mem_ctx); - - /* Making source be const is just extra documentation. - */ - v.run(const_cast<exec_list *>(source)); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file ir_import_prototypes.cpp
+ * Import function prototypes from one IR tree into another.
+ *
+ * \author Ian Romanick
+ */
+#include "ir.h"
+#include "glsl_symbol_table.h"
+
+/**
+ * Visitor used to import function prototypes
+ *
+ * Normally the \c clone method of either \c ir_function or
+ * \c ir_function_signature could be used. However, we don't want a complete
+ * clone of the \c ir_function_signature. We want everything \b except the
+ * body of the function.
+ */
+class import_prototype_visitor : public ir_hierarchical_visitor {
+public:
+ /**
+ */
+ import_prototype_visitor(exec_list *list, glsl_symbol_table *symbols,
+ void *mem_ctx)
+ {
+ this->mem_ctx = mem_ctx;
+ this->list = list;
+ this->symbols = symbols;
+ this->function = NULL;
+ }
+
+ virtual ir_visitor_status visit_enter(ir_function *ir)
+ {
+ assert(this->function == NULL);
+
+ this->function = this->symbols->get_function(ir->name);
+ if (!this->function) {
+ this->function = new(this->mem_ctx) ir_function(ir->name);
+
+ list->push_tail(this->function);
+
+ /* Add the new function to the symbol table.
+ */
+ this->symbols->add_function(this->function);
+ }
+ return visit_continue;
+ }
+
+ virtual ir_visitor_status visit_leave(ir_function *ir)
+ {
+ (void) ir;
+ assert(this->function != NULL);
+
+ this->function = NULL;
+ return visit_continue;
+ }
+
+ ir_visitor_status visit_enter(ir_function_signature *ir)
+ {
+ assert(this->function != NULL);
+
+ ir_function_signature *copy = ir->clone_prototype(mem_ctx, NULL);
+
+ this->function->add_signature(copy);
+
+ /* Do not process child nodes of the ir_function_signature. There can
+ * never be any nodes inside the ir_function_signature that we care
+ * about. Instead continue with the next sibling.
+ */
+ return visit_continue_with_parent;
+ }
+
+private:
+ exec_list *list;
+ ir_function *function;
+ glsl_symbol_table *symbols;
+ void *mem_ctx;
+};
+
+
+/**
+ * Import function prototypes from one IR tree into another
+ *
+ * \param source Source instruction stream containing functions whose
+ * prototypes are to be imported
+ * \param dest Destination instruction stream where new \c ir_function and
+ * \c ir_function_signature nodes will be stored
+ * \param symbols Symbol table where new functions will be stored
+ * \param mem_ctx ralloc memory context used for new allocations
+ */
+void
+import_prototypes(const exec_list *source, exec_list *dest,
+ glsl_symbol_table *symbols, void *mem_ctx)
+{
+ import_prototype_visitor v(dest, symbols, mem_ctx);
+
+ /* Making source be const is just extra documentation.
+ */
+ v.run(const_cast<exec_list *>(source));
+}
diff --git a/mesalib/src/glsl/link_functions.cpp b/mesalib/src/glsl/link_functions.cpp index ae8818be8..76687b6be 100644 --- a/mesalib/src/glsl/link_functions.cpp +++ b/mesalib/src/glsl/link_functions.cpp @@ -1,272 +1,272 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include "main/core.h" -#include "glsl_symbol_table.h" -#include "glsl_parser_extras.h" -#include "ir.h" -#include "program.h" -#include "program/hash_table.h" -#include "linker.h" - -static ir_function_signature * -find_matching_signature(const char *name, const exec_list *actual_parameters, - gl_shader **shader_list, unsigned num_shaders); - -class call_link_visitor : public ir_hierarchical_visitor { -public: - call_link_visitor(gl_shader_program *prog, gl_shader *linked, - gl_shader **shader_list, unsigned num_shaders) - { - this->prog = prog; - this->shader_list = shader_list; - this->num_shaders = num_shaders; - this->success = true; - this->linked = linked; - - this->locals = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - } - - ~call_link_visitor() - { - hash_table_dtor(this->locals); - } - - virtual ir_visitor_status visit(ir_variable *ir) - { - hash_table_insert(locals, ir, ir); - return visit_continue; - } - - virtual ir_visitor_status visit_enter(ir_call *ir) - { - /* If ir is an ir_call from a function that was imported from another - * shader callee will point to an ir_function_signature in the original - * shader. In this case the function signature MUST NOT BE MODIFIED. - * Doing so will modify the original shader. This may prevent that - * shader from being linkable in other programs. - */ - const ir_function_signature *const callee = ir->get_callee(); - assert(callee != NULL); - const char *const name = callee->function_name(); - - /* Determine if the requested function signature already exists in the - * final linked shader. If it does, use it as the target of the call. - */ - ir_function_signature *sig = - find_matching_signature(name, &callee->parameters, &linked, 1); - if (sig != NULL) { - ir->set_callee(sig); - return visit_continue; - } - - /* Try to find the signature in one of the other shaders that is being - * linked. If it's not found there, return an error. - */ - sig = find_matching_signature(name, &ir->actual_parameters, shader_list, - num_shaders); - if (sig == NULL) { - /* FINISHME: Log the full signature of unresolved function. - */ - linker_error_printf(this->prog, "unresolved reference to function " - "`%s'\n", name); - this->success = false; - return visit_stop; - } - - /* Find the prototype information in the linked shader. Generate any - * details that may be missing. - */ - ir_function *f = linked->symbols->get_function(name); - if (f == NULL) { - f = new(linked) ir_function(name); - - /* Add the new function to the linked IR. - */ - linked->symbols->add_function(f); - linked->ir->push_head(f); - } - - ir_function_signature *linked_sig = - f->exact_matching_signature(&callee->parameters); - if (linked_sig == NULL) { - linked_sig = new(linked) ir_function_signature(callee->return_type); - f->add_signature(linked_sig); - } - - /* At this point linked_sig and called may be the same. If ir is an - * ir_call from linked then linked_sig and callee will be - * ir_function_signatures that have no definitions (is_defined is false). - */ - assert(!linked_sig->is_defined); - assert(linked_sig->body.is_empty()); - - /* Create an in-place clone of the function definition. This multistep - * process introduces some complexity here, but it has some advantages. - * The parameter list and the and function body are cloned separately. - * The clone of the parameter list is used to prime the hashtable used - * to replace variable references in the cloned body. - * - * The big advantage is that the ir_function_signature does not change. - * This means that we don't have to process the rest of the IR tree to - * patch ir_call nodes. In addition, there is no way to remove or - * replace signature stored in a function. One could easily be added, - * but this avoids the need. - */ - struct hash_table *ht = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - exec_list formal_parameters; - foreach_list_const(node, &sig->parameters) { - const ir_instruction *const original = (ir_instruction *) node; - assert(const_cast<ir_instruction *>(original)->as_variable()); - - ir_instruction *copy = original->clone(linked, ht); - formal_parameters.push_tail(copy); - } - - linked_sig->replace_parameters(&formal_parameters); - - foreach_list_const(node, &sig->body) { - const ir_instruction *const original = (ir_instruction *) node; - - ir_instruction *copy = original->clone(linked, ht); - linked_sig->body.push_tail(copy); - } - - linked_sig->is_defined = true; - hash_table_dtor(ht); - - /* Patch references inside the function to things outside the function - * (i.e., function calls and global variables). - */ - linked_sig->accept(this); - - ir->set_callee(linked_sig); - - return visit_continue; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (hash_table_find(locals, ir->var) == NULL) { - /* The non-function variable must be a global, so try to find the - * variable in the shader's symbol table. If the variable is not - * found, then it's a global that *MUST* be defined in the original - * shader. - */ - ir_variable *var = linked->symbols->get_variable(ir->var->name); - if (var == NULL) { - /* Clone the ir_variable that the dereference already has and add - * it to the linked shader. - */ - var = ir->var->clone(linked, NULL); - linked->symbols->add_variable(var); - linked->ir->push_head(var); - } else if (var->type->is_array()) { - /* It is possible to have a global array declared in multiple - * shaders without a size. The array is implicitly sized by the - * maximal access to it in *any* shader. Because of this, we - * need to track the maximal access to the array as linking pulls - * more functions in that access the array. - */ - var->max_array_access = - MAX2(var->max_array_access, ir->var->max_array_access); - - if (var->type->length == 0 && ir->var->type->length != 0) - var->type = ir->var->type; - } - - ir->var = var; - } - - return visit_continue; - } - - /** Was function linking successful? */ - bool success; - -private: - /** - * Shader program being linked - * - * This is only used for logging error messages. - */ - gl_shader_program *prog; - - /** List of shaders available for linking. */ - gl_shader **shader_list; - - /** Number of shaders available for linking. */ - unsigned num_shaders; - - /** - * Final linked shader - * - * This is used two ways. It is used to find global variables in the - * linked shader that are accessed by the function. It is also used to add - * global variables from the shader where the function originated. - */ - gl_shader *linked; - - /** - * Table of variables local to the function. - */ - hash_table *locals; -}; - - -/** - * Searches a list of shaders for a particular function definition - */ -ir_function_signature * -find_matching_signature(const char *name, const exec_list *actual_parameters, - gl_shader **shader_list, unsigned num_shaders) -{ - for (unsigned i = 0; i < num_shaders; i++) { - ir_function *const f = shader_list[i]->symbols->get_function(name); - - if (f == NULL) - continue; - - ir_function_signature *sig = f->matching_signature(actual_parameters); - - if ((sig == NULL) || !sig->is_defined) - continue; - - return sig; - } - - return NULL; -} - - -bool -link_function_calls(gl_shader_program *prog, gl_shader *main, - gl_shader **shader_list, unsigned num_shaders) -{ - call_link_visitor v(prog, main, shader_list, num_shaders); - - v.run(main->ir); - return v.success; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "main/core.h"
+#include "glsl_symbol_table.h"
+#include "glsl_parser_extras.h"
+#include "ir.h"
+#include "program.h"
+#include "program/hash_table.h"
+#include "linker.h"
+
+static ir_function_signature *
+find_matching_signature(const char *name, const exec_list *actual_parameters,
+ gl_shader **shader_list, unsigned num_shaders);
+
+class call_link_visitor : public ir_hierarchical_visitor {
+public:
+ call_link_visitor(gl_shader_program *prog, gl_shader *linked,
+ gl_shader **shader_list, unsigned num_shaders)
+ {
+ this->prog = prog;
+ this->shader_list = shader_list;
+ this->num_shaders = num_shaders;
+ this->success = true;
+ this->linked = linked;
+
+ this->locals = hash_table_ctor(0, hash_table_pointer_hash,
+ hash_table_pointer_compare);
+ }
+
+ ~call_link_visitor()
+ {
+ hash_table_dtor(this->locals);
+ }
+
+ virtual ir_visitor_status visit(ir_variable *ir)
+ {
+ hash_table_insert(locals, ir, ir);
+ return visit_continue;
+ }
+
+ virtual ir_visitor_status visit_enter(ir_call *ir)
+ {
+ /* If ir is an ir_call from a function that was imported from another
+ * shader callee will point to an ir_function_signature in the original
+ * shader. In this case the function signature MUST NOT BE MODIFIED.
+ * Doing so will modify the original shader. This may prevent that
+ * shader from being linkable in other programs.
+ */
+ const ir_function_signature *const callee = ir->get_callee();
+ assert(callee != NULL);
+ const char *const name = callee->function_name();
+
+ /* Determine if the requested function signature already exists in the
+ * final linked shader. If it does, use it as the target of the call.
+ */
+ ir_function_signature *sig =
+ find_matching_signature(name, &callee->parameters, &linked, 1);
+ if (sig != NULL) {
+ ir->set_callee(sig);
+ return visit_continue;
+ }
+
+ /* Try to find the signature in one of the other shaders that is being
+ * linked. If it's not found there, return an error.
+ */
+ sig = find_matching_signature(name, &ir->actual_parameters, shader_list,
+ num_shaders);
+ if (sig == NULL) {
+ /* FINISHME: Log the full signature of unresolved function.
+ */
+ linker_error_printf(this->prog, "unresolved reference to function "
+ "`%s'\n", name);
+ this->success = false;
+ return visit_stop;
+ }
+
+ /* Find the prototype information in the linked shader. Generate any
+ * details that may be missing.
+ */
+ ir_function *f = linked->symbols->get_function(name);
+ if (f == NULL) {
+ f = new(linked) ir_function(name);
+
+ /* Add the new function to the linked IR.
+ */
+ linked->symbols->add_function(f);
+ linked->ir->push_head(f);
+ }
+
+ ir_function_signature *linked_sig =
+ f->exact_matching_signature(&callee->parameters);
+ if (linked_sig == NULL) {
+ linked_sig = new(linked) ir_function_signature(callee->return_type);
+ f->add_signature(linked_sig);
+ }
+
+ /* At this point linked_sig and called may be the same. If ir is an
+ * ir_call from linked then linked_sig and callee will be
+ * ir_function_signatures that have no definitions (is_defined is false).
+ */
+ assert(!linked_sig->is_defined);
+ assert(linked_sig->body.is_empty());
+
+ /* Create an in-place clone of the function definition. This multistep
+ * process introduces some complexity here, but it has some advantages.
+ * The parameter list and the and function body are cloned separately.
+ * The clone of the parameter list is used to prime the hashtable used
+ * to replace variable references in the cloned body.
+ *
+ * The big advantage is that the ir_function_signature does not change.
+ * This means that we don't have to process the rest of the IR tree to
+ * patch ir_call nodes. In addition, there is no way to remove or
+ * replace signature stored in a function. One could easily be added,
+ * but this avoids the need.
+ */
+ struct hash_table *ht = hash_table_ctor(0, hash_table_pointer_hash,
+ hash_table_pointer_compare);
+ exec_list formal_parameters;
+ foreach_list_const(node, &sig->parameters) {
+ const ir_instruction *const original = (ir_instruction *) node;
+ assert(const_cast<ir_instruction *>(original)->as_variable());
+
+ ir_instruction *copy = original->clone(linked, ht);
+ formal_parameters.push_tail(copy);
+ }
+
+ linked_sig->replace_parameters(&formal_parameters);
+
+ foreach_list_const(node, &sig->body) {
+ const ir_instruction *const original = (ir_instruction *) node;
+
+ ir_instruction *copy = original->clone(linked, ht);
+ linked_sig->body.push_tail(copy);
+ }
+
+ linked_sig->is_defined = true;
+ hash_table_dtor(ht);
+
+ /* Patch references inside the function to things outside the function
+ * (i.e., function calls and global variables).
+ */
+ linked_sig->accept(this);
+
+ ir->set_callee(linked_sig);
+
+ return visit_continue;
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ if (hash_table_find(locals, ir->var) == NULL) {
+ /* The non-function variable must be a global, so try to find the
+ * variable in the shader's symbol table. If the variable is not
+ * found, then it's a global that *MUST* be defined in the original
+ * shader.
+ */
+ ir_variable *var = linked->symbols->get_variable(ir->var->name);
+ if (var == NULL) {
+ /* Clone the ir_variable that the dereference already has and add
+ * it to the linked shader.
+ */
+ var = ir->var->clone(linked, NULL);
+ linked->symbols->add_variable(var);
+ linked->ir->push_head(var);
+ } else if (var->type->is_array()) {
+ /* It is possible to have a global array declared in multiple
+ * shaders without a size. The array is implicitly sized by the
+ * maximal access to it in *any* shader. Because of this, we
+ * need to track the maximal access to the array as linking pulls
+ * more functions in that access the array.
+ */
+ var->max_array_access =
+ MAX2(var->max_array_access, ir->var->max_array_access);
+
+ if (var->type->length == 0 && ir->var->type->length != 0)
+ var->type = ir->var->type;
+ }
+
+ ir->var = var;
+ }
+
+ return visit_continue;
+ }
+
+ /** Was function linking successful? */
+ bool success;
+
+private:
+ /**
+ * Shader program being linked
+ *
+ * This is only used for logging error messages.
+ */
+ gl_shader_program *prog;
+
+ /** List of shaders available for linking. */
+ gl_shader **shader_list;
+
+ /** Number of shaders available for linking. */
+ unsigned num_shaders;
+
+ /**
+ * Final linked shader
+ *
+ * This is used two ways. It is used to find global variables in the
+ * linked shader that are accessed by the function. It is also used to add
+ * global variables from the shader where the function originated.
+ */
+ gl_shader *linked;
+
+ /**
+ * Table of variables local to the function.
+ */
+ hash_table *locals;
+};
+
+
+/**
+ * Searches a list of shaders for a particular function definition
+ */
+ir_function_signature *
+find_matching_signature(const char *name, const exec_list *actual_parameters,
+ gl_shader **shader_list, unsigned num_shaders)
+{
+ for (unsigned i = 0; i < num_shaders; i++) {
+ ir_function *const f = shader_list[i]->symbols->get_function(name);
+
+ if (f == NULL)
+ continue;
+
+ ir_function_signature *sig = f->matching_signature(actual_parameters);
+
+ if ((sig == NULL) || !sig->is_defined)
+ continue;
+
+ return sig;
+ }
+
+ return NULL;
+}
+
+
+bool
+link_function_calls(gl_shader_program *prog, gl_shader *main,
+ gl_shader **shader_list, unsigned num_shaders)
+{
+ call_link_visitor v(prog, main, shader_list, num_shaders);
+
+ v.run(main->ir);
+ return v.success;
+}
diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp index 7db5c5e8d..7760b4f2c 100644 --- a/mesalib/src/glsl/linker.cpp +++ b/mesalib/src/glsl/linker.cpp @@ -1,1709 +1,1709 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file linker.cpp - * GLSL linker implementation - * - * Given a set of shaders that are to be linked to generate a final program, - * there are three distinct stages. - * - * In the first stage shaders are partitioned into groups based on the shader - * type. All shaders of a particular type (e.g., vertex shaders) are linked - * together. - * - * - Undefined references in each shader are resolve to definitions in - * another shader. - * - Types and qualifiers of uniforms, outputs, and global variables defined - * in multiple shaders with the same name are verified to be the same. - * - Initializers for uniforms and global variables defined - * in multiple shaders with the same name are verified to be the same. - * - * The result, in the terminology of the GLSL spec, is a set of shader - * executables for each processing unit. - * - * After the first stage is complete, a series of semantic checks are performed - * on each of the shader executables. - * - * - Each shader executable must define a \c main function. - * - Each vertex shader executable must write to \c gl_Position. - * - Each fragment shader executable must write to either \c gl_FragData or - * \c gl_FragColor. - * - * In the final stage individual shader executables are linked to create a - * complete exectuable. - * - * - Types of uniforms defined in multiple shader stages with the same name - * are verified to be the same. - * - Initializers for uniforms defined in multiple shader stages with the - * same name are verified to be the same. - * - Types and qualifiers of outputs defined in one stage are verified to - * be the same as the types and qualifiers of inputs defined with the same - * name in a later stage. - * - * \author Ian Romanick <ian.d.romanick@intel.com> - */ - -#include "main/core.h" -#include "glsl_symbol_table.h" -#include "ir.h" -#include "program.h" -#include "program/hash_table.h" -#include "linker.h" -#include "ir_optimization.h" - -extern "C" { -#include "main/shaderobj.h" -} - -/** - * Visitor that determines whether or not a variable is ever written. - */ -class find_assignment_visitor : public ir_hierarchical_visitor { -public: - find_assignment_visitor(const char *name) - : name(name), found(false) - { - /* empty */ - } - - virtual ir_visitor_status visit_enter(ir_assignment *ir) - { - ir_variable *const var = ir->lhs->variable_referenced(); - - if (strcmp(name, var->name) == 0) { - found = true; - return visit_stop; - } - - return visit_continue_with_parent; - } - - virtual ir_visitor_status visit_enter(ir_call *ir) - { - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *sig_param = (ir_variable *)sig_iter.get(); - - if (sig_param->mode == ir_var_out || - sig_param->mode == ir_var_inout) { - ir_variable *var = param_rval->variable_referenced(); - if (var && strcmp(name, var->name) == 0) { - found = true; - return visit_stop; - } - } - sig_iter.next(); - } - - return visit_continue_with_parent; - } - - bool variable_found() - { - return found; - } - -private: - const char *name; /**< Find writes to a variable with this name. */ - bool found; /**< Was a write to the variable found? */ -}; - - -/** - * Visitor that determines whether or not a variable is ever read. - */ -class find_deref_visitor : public ir_hierarchical_visitor { -public: - find_deref_visitor(const char *name) - : name(name), found(false) - { - /* empty */ - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (strcmp(this->name, ir->var->name) == 0) { - this->found = true; - return visit_stop; - } - - return visit_continue; - } - - bool variable_found() const - { - return this->found; - } - -private: - const char *name; /**< Find writes to a variable with this name. */ - bool found; /**< Was a write to the variable found? */ -}; - - -void -linker_error_printf(gl_shader_program *prog, const char *fmt, ...) -{ - va_list ap; - - ralloc_strcat(&prog->InfoLog, "error: "); - va_start(ap, fmt); - ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); - va_end(ap); -} - - -void -invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode, - int generic_base) -{ - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != (unsigned) mode)) - continue; - - /* Only assign locations for generic attributes / varyings / etc. - */ - if ((var->location >= generic_base) && !var->explicit_location) - var->location = -1; - } -} - - -/** - * Determine the number of attribute slots required for a particular type - * - * This code is here because it implements the language rules of a specific - * GLSL version. Since it's a property of the language and not a property of - * types in general, it doesn't really belong in glsl_type. - */ -unsigned -count_attribute_slots(const glsl_type *t) -{ - /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec: - * - * "A scalar input counts the same amount against this limit as a vec4, - * so applications may want to consider packing groups of four - * unrelated float inputs together into a vector to better utilize the - * capabilities of the underlying hardware. A matrix input will use up - * multiple locations. The number of locations used will equal the - * number of columns in the matrix." - * - * The spec does not explicitly say how arrays are counted. However, it - * should be safe to assume the total number of slots consumed by an array - * is the number of entries in the array multiplied by the number of slots - * consumed by a single element of the array. - */ - - if (t->is_array()) - return t->array_size() * count_attribute_slots(t->element_type()); - - if (t->is_matrix()) - return t->matrix_columns; - - return 1; -} - - -/** - * Verify that a vertex shader executable meets all semantic requirements - * - * \param shader Vertex shader executable to be verified - */ -bool -validate_vertex_shader_executable(struct gl_shader_program *prog, - struct gl_shader *shader) -{ - if (shader == NULL) - return true; - - find_assignment_visitor find("gl_Position"); - find.run(shader->ir); - if (!find.variable_found()) { - linker_error_printf(prog, - "vertex shader does not write to `gl_Position'\n"); - return false; - } - - return true; -} - - -/** - * Verify that a fragment shader executable meets all semantic requirements - * - * \param shader Fragment shader executable to be verified - */ -bool -validate_fragment_shader_executable(struct gl_shader_program *prog, - struct gl_shader *shader) -{ - if (shader == NULL) - return true; - - find_assignment_visitor frag_color("gl_FragColor"); - find_assignment_visitor frag_data("gl_FragData"); - - frag_color.run(shader->ir); - frag_data.run(shader->ir); - - if (frag_color.variable_found() && frag_data.variable_found()) { - linker_error_printf(prog, "fragment shader writes to both " - "`gl_FragColor' and `gl_FragData'\n"); - return false; - } - - return true; -} - - -/** - * Generate a string describing the mode of a variable - */ -static const char * -mode_string(const ir_variable *var) -{ - switch (var->mode) { - case ir_var_auto: - return (var->read_only) ? "global constant" : "global variable"; - - case ir_var_uniform: return "uniform"; - case ir_var_in: return "shader input"; - case ir_var_out: return "shader output"; - case ir_var_inout: return "shader inout"; - - case ir_var_const_in: - case ir_var_temporary: - default: - assert(!"Should not get here."); - return "invalid variable"; - } -} - - -/** - * Perform validation of global variables used across multiple shaders - */ -bool -cross_validate_globals(struct gl_shader_program *prog, - struct gl_shader **shader_list, - unsigned num_shaders, - bool uniforms_only) -{ - /* Examine all of the uniforms in all of the shaders and cross validate - * them. - */ - glsl_symbol_table variables; - for (unsigned i = 0; i < num_shaders; i++) { - if (shader_list[i] == NULL) - continue; - - foreach_list(node, shader_list[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if (var == NULL) - continue; - - if (uniforms_only && (var->mode != ir_var_uniform)) - continue; - - /* Don't cross validate temporaries that are at global scope. These - * will eventually get pulled into the shaders 'main'. - */ - if (var->mode == ir_var_temporary) - continue; - - /* If a global with this name has already been seen, verify that the - * new instance has the same type. In addition, if the globals have - * initializers, the values of the initializers must be the same. - */ - ir_variable *const existing = variables.get_variable(var->name); - if (existing != NULL) { - if (var->type != existing->type) { - /* Consider the types to be "the same" if both types are arrays - * of the same type and one of the arrays is implicitly sized. - * In addition, set the type of the linked variable to the - * explicitly sized array. - */ - if (var->type->is_array() - && existing->type->is_array() - && (var->type->fields.array == existing->type->fields.array) - && ((var->type->length == 0) - || (existing->type->length == 0))) { - if (var->type->length != 0) { - existing->type = var->type; - } - } else { - linker_error_printf(prog, "%s `%s' declared as type " - "`%s' and type `%s'\n", - mode_string(var), - var->name, var->type->name, - existing->type->name); - return false; - } - } - - if (var->explicit_location) { - if (existing->explicit_location - && (var->location != existing->location)) { - linker_error_printf(prog, "explicit locations for %s " - "`%s' have differing values\n", - mode_string(var), var->name); - return false; - } - - existing->location = var->location; - existing->explicit_location = true; - } - - /* Validate layout qualifiers for gl_FragDepth. - * - * From the AMD_conservative_depth spec: - * "If gl_FragDepth is redeclared in any fragment shader in - * a program, it must be redeclared in all fragment shaders in that - * program that have static assignments to gl_FragDepth. All - * redeclarations of gl_FragDepth in all fragment shaders in - * a single program must have the same set of qualifiers." - */ - if (strcmp(var->name, "gl_FragDepth") == 0) { - bool layout_declared = var->depth_layout != ir_depth_layout_none; - bool layout_differs = var->depth_layout != existing->depth_layout; - if (layout_declared && layout_differs) { - linker_error_printf(prog, - "All redeclarations of gl_FragDepth in all fragment shaders " - "in a single program must have the same set of qualifiers."); - } - if (var->used && layout_differs) { - linker_error_printf(prog, - "If gl_FragDepth is redeclared with a layout qualifier in" - "any fragment shader, it must be redeclared with the same" - "layout qualifier in all fragment shaders that have" - "assignments to gl_FragDepth"); - } - } - - /* FINISHME: Handle non-constant initializers. - */ - if (var->constant_value != NULL) { - if (existing->constant_value != NULL) { - if (!var->constant_value->has_value(existing->constant_value)) { - linker_error_printf(prog, "initializers for %s " - "`%s' have differing values\n", - mode_string(var), var->name); - return false; - } - } else - /* If the first-seen instance of a particular uniform did not - * have an initializer but a later instance does, copy the - * initializer to the version stored in the symbol table. - */ - /* FINISHME: This is wrong. The constant_value field should - * FINISHME: not be modified! Imagine a case where a shader - * FINISHME: without an initializer is linked in two different - * FINISHME: programs with shaders that have differing - * FINISHME: initializers. Linking with the first will - * FINISHME: modify the shader, and linking with the second - * FINISHME: will fail. - */ - existing->constant_value = - var->constant_value->clone(ralloc_parent(existing), NULL); - } - - if (existing->invariant != var->invariant) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching invariant qualifiers\n", - mode_string(var), var->name); - return false; - } - if (existing->centroid != var->centroid) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching centroid qualifiers\n", - mode_string(var), var->name); - return false; - } - } else - variables.add_variable(var); - } - } - - return true; -} - - -/** - * Perform validation of uniforms used across multiple shader stages - */ -bool -cross_validate_uniforms(struct gl_shader_program *prog) -{ - return cross_validate_globals(prog, prog->_LinkedShaders, - MESA_SHADER_TYPES, true); -} - - -/** - * Validate that outputs from one stage match inputs of another - */ -bool -cross_validate_outputs_to_inputs(struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) -{ - glsl_symbol_table parameters; - /* FINISHME: Figure these out dynamically. */ - const char *const producer_stage = "vertex"; - const char *const consumer_stage = "fragment"; - - /* Find all shader outputs in the "producer" stage. - */ - foreach_list(node, producer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - /* FINISHME: For geometry shaders, this should also look for inout - * FINISHME: variables. - */ - if ((var == NULL) || (var->mode != ir_var_out)) - continue; - - parameters.add_variable(var); - } - - - /* Find all shader inputs in the "consumer" stage. Any variables that have - * matching outputs already in the symbol table must have the same type and - * qualifiers. - */ - foreach_list(node, consumer->ir) { - ir_variable *const input = ((ir_instruction *) node)->as_variable(); - - /* FINISHME: For geometry shaders, this should also look for inout - * FINISHME: variables. - */ - if ((input == NULL) || (input->mode != ir_var_in)) - continue; - - ir_variable *const output = parameters.get_variable(input->name); - if (output != NULL) { - /* Check that the types match between stages. - */ - if (input->type != output->type) { - /* There is a bit of a special case for gl_TexCoord. This - * built-in is unsized by default. Appliations that variable - * access it must redeclare it with a size. There is some - * language in the GLSL spec that implies the fragment shader - * and vertex shader do not have to agree on this size. Other - * driver behave this way, and one or two applications seem to - * rely on it. - * - * Neither declaration needs to be modified here because the array - * sizes are fixed later when update_array_sizes is called. - * - * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec: - * - * "Unlike user-defined varying variables, the built-in - * varying variables don't have a strict one-to-one - * correspondence between the vertex language and the - * fragment language." - */ - if (!output->type->is_array() - || (strncmp("gl_", output->name, 3) != 0)) { - linker_error_printf(prog, - "%s shader output `%s' declared as " - "type `%s', but %s shader input declared " - "as type `%s'\n", - producer_stage, output->name, - output->type->name, - consumer_stage, input->type->name); - return false; - } - } - - /* Check that all of the qualifiers match between stages. - */ - if (input->centroid != output->centroid) { - linker_error_printf(prog, - "%s shader output `%s' %s centroid qualifier, " - "but %s shader input %s centroid qualifier\n", - producer_stage, - output->name, - (output->centroid) ? "has" : "lacks", - consumer_stage, - (input->centroid) ? "has" : "lacks"); - return false; - } - - if (input->invariant != output->invariant) { - linker_error_printf(prog, - "%s shader output `%s' %s invariant qualifier, " - "but %s shader input %s invariant qualifier\n", - producer_stage, - output->name, - (output->invariant) ? "has" : "lacks", - consumer_stage, - (input->invariant) ? "has" : "lacks"); - return false; - } - - if (input->interpolation != output->interpolation) { - linker_error_printf(prog, - "%s shader output `%s' specifies %s " - "interpolation qualifier, " - "but %s shader input specifies %s " - "interpolation qualifier\n", - producer_stage, - output->name, - output->interpolation_string(), - consumer_stage, - input->interpolation_string()); - return false; - } - } - } - - return true; -} - - -/** - * Populates a shaders symbol table with all global declarations - */ -static void -populate_symbol_table(gl_shader *sh) -{ - sh->symbols = new(sh) glsl_symbol_table; - - foreach_list(node, sh->ir) { - ir_instruction *const inst = (ir_instruction *) node; - ir_variable *var; - ir_function *func; - - if ((func = inst->as_function()) != NULL) { - sh->symbols->add_function(func); - } else if ((var = inst->as_variable()) != NULL) { - sh->symbols->add_variable(var); - } - } -} - - -/** - * Remap variables referenced in an instruction tree - * - * This is used when instruction trees are cloned from one shader and placed in - * another. These trees will contain references to \c ir_variable nodes that - * do not exist in the target shader. This function finds these \c ir_variable - * references and replaces the references with matching variables in the target - * shader. - * - * If there is no matching variable in the target shader, a clone of the - * \c ir_variable is made and added to the target shader. The new variable is - * added to \b both the instruction stream and the symbol table. - * - * \param inst IR tree that is to be processed. - * \param symbols Symbol table containing global scope symbols in the - * linked shader. - * \param instructions Instruction stream where new variable declarations - * should be added. - */ -void -remap_variables(ir_instruction *inst, struct gl_shader *target, - hash_table *temps) -{ - class remap_visitor : public ir_hierarchical_visitor { - public: - remap_visitor(struct gl_shader *target, - hash_table *temps) - { - this->target = target; - this->symbols = target->symbols; - this->instructions = target->ir; - this->temps = temps; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - if (ir->var->mode == ir_var_temporary) { - ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var); - - assert(var != NULL); - ir->var = var; - return visit_continue; - } - - ir_variable *const existing = - this->symbols->get_variable(ir->var->name); - if (existing != NULL) - ir->var = existing; - else { - ir_variable *copy = ir->var->clone(this->target, NULL); - - this->symbols->add_variable(copy); - this->instructions->push_head(copy); - ir->var = copy; - } - - return visit_continue; - } - - private: - struct gl_shader *target; - glsl_symbol_table *symbols; - exec_list *instructions; - hash_table *temps; - }; - - remap_visitor v(target, temps); - - inst->accept(&v); -} - - -/** - * Move non-declarations from one instruction stream to another - * - * The intended usage pattern of this function is to pass the pointer to the - * head sentinel of a list (i.e., a pointer to the list cast to an \c exec_node - * pointer) for \c last and \c false for \c make_copies on the first - * call. Successive calls pass the return value of the previous call for - * \c last and \c true for \c make_copies. - * - * \param instructions Source instruction stream - * \param last Instruction after which new instructions should be - * inserted in the target instruction stream - * \param make_copies Flag selecting whether instructions in \c instructions - * should be copied (via \c ir_instruction::clone) into the - * target list or moved. - * - * \return - * The new "last" instruction in the target instruction stream. This pointer - * is suitable for use as the \c last parameter of a later call to this - * function. - */ -exec_node * -move_non_declarations(exec_list *instructions, exec_node *last, - bool make_copies, gl_shader *target) -{ - hash_table *temps = NULL; - - if (make_copies) - temps = hash_table_ctor(0, hash_table_pointer_hash, - hash_table_pointer_compare); - - foreach_list_safe(node, instructions) { - ir_instruction *inst = (ir_instruction *) node; - - if (inst->as_function()) - continue; - - ir_variable *var = inst->as_variable(); - if ((var != NULL) && (var->mode != ir_var_temporary)) - continue; - - assert(inst->as_assignment() - || ((var != NULL) && (var->mode == ir_var_temporary))); - - if (make_copies) { - inst = inst->clone(target, NULL); - - if (var != NULL) - hash_table_insert(temps, inst, var); - else - remap_variables(inst, target, temps); - } else { - inst->remove(); - } - - last->insert_after(inst); - last = inst; - } - - if (make_copies) - hash_table_dtor(temps); - - return last; -} - -/** - * Get the function signature for main from a shader - */ -static ir_function_signature * -get_main_function_signature(gl_shader *sh) -{ - ir_function *const f = sh->symbols->get_function("main"); - if (f != NULL) { - exec_list void_parameters; - - /* Look for the 'void main()' signature and ensure that it's defined. - * This keeps the linker from accidentally pick a shader that just - * contains a prototype for main. - * - * We don't have to check for multiple definitions of main (in multiple - * shaders) because that would have already been caught above. - */ - ir_function_signature *sig = f->matching_signature(&void_parameters); - if ((sig != NULL) && sig->is_defined) { - return sig; - } - } - - return NULL; -} - - -/** - * Combine a group of shaders for a single stage to generate a linked shader - * - * \note - * If this function is supplied a single shader, it is cloned, and the new - * shader is returned. - */ -static struct gl_shader * -link_intrastage_shaders(void *mem_ctx, - struct gl_context *ctx, - struct gl_shader_program *prog, - struct gl_shader **shader_list, - unsigned num_shaders) -{ - /* Check that global variables defined in multiple shaders are consistent. - */ - if (!cross_validate_globals(prog, shader_list, num_shaders, false)) - return NULL; - - /* Check that there is only a single definition of each function signature - * across all shaders. - */ - for (unsigned i = 0; i < (num_shaders - 1); i++) { - foreach_list(node, shader_list[i]->ir) { - ir_function *const f = ((ir_instruction *) node)->as_function(); - - if (f == NULL) - continue; - - for (unsigned j = i + 1; j < num_shaders; j++) { - ir_function *const other = - shader_list[j]->symbols->get_function(f->name); - - /* If the other shader has no function (and therefore no function - * signatures) with the same name, skip to the next shader. - */ - if (other == NULL) - continue; - - foreach_iter (exec_list_iterator, iter, *f) { - ir_function_signature *sig = - (ir_function_signature *) iter.get(); - - if (!sig->is_defined || sig->is_builtin) - continue; - - ir_function_signature *other_sig = - other->exact_matching_signature(& sig->parameters); - - if ((other_sig != NULL) && other_sig->is_defined - && !other_sig->is_builtin) { - linker_error_printf(prog, - "function `%s' is multiply defined", - f->name); - return NULL; - } - } - } - } - } - - /* Find the shader that defines main, and make a clone of it. - * - * Starting with the clone, search for undefined references. If one is - * found, find the shader that defines it. Clone the reference and add - * it to the shader. Repeat until there are no undefined references or - * until a reference cannot be resolved. - */ - gl_shader *main = NULL; - for (unsigned i = 0; i < num_shaders; i++) { - if (get_main_function_signature(shader_list[i]) != NULL) { - main = shader_list[i]; - break; - } - } - - if (main == NULL) { - linker_error_printf(prog, "%s shader lacks `main'\n", - (shader_list[0]->Type == GL_VERTEX_SHADER) - ? "vertex" : "fragment"); - return NULL; - } - - gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type); - linked->ir = new(linked) exec_list; - clone_ir_list(mem_ctx, linked->ir, main->ir); - - populate_symbol_table(linked); - - /* The a pointer to the main function in the final linked shader (i.e., the - * copy of the original shader that contained the main function). - */ - ir_function_signature *const main_sig = get_main_function_signature(linked); - - /* Move any instructions other than variable declarations or function - * declarations into main. - */ - exec_node *insertion_point = - move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false, - linked); - - for (unsigned i = 0; i < num_shaders; i++) { - if (shader_list[i] == main) - continue; - - insertion_point = move_non_declarations(shader_list[i]->ir, - insertion_point, true, linked); - } - - /* Resolve initializers for global variables in the linked shader. - */ - unsigned num_linking_shaders = num_shaders; - for (unsigned i = 0; i < num_shaders; i++) - num_linking_shaders += shader_list[i]->num_builtins_to_link; - - gl_shader **linking_shaders = - (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *)); - - memcpy(linking_shaders, shader_list, - sizeof(linking_shaders[0]) * num_shaders); - - unsigned idx = num_shaders; - for (unsigned i = 0; i < num_shaders; i++) { - memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link, - sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link); - idx += shader_list[i]->num_builtins_to_link; - } - - assert(idx == num_linking_shaders); - - if (!link_function_calls(prog, linked, linking_shaders, - num_linking_shaders)) { - ctx->Driver.DeleteShader(ctx, linked); - linked = NULL; - } - - free(linking_shaders); - - /* Make a pass over all variable declarations to ensure that arrays with - * unspecified sizes have a size specified. The size is inferred from the - * max_array_access field. - */ - if (linked != NULL) { - class array_sizing_visitor : public ir_hierarchical_visitor { - public: - virtual ir_visitor_status visit(ir_variable *var) - { - if (var->type->is_array() && (var->type->length == 0)) { - const glsl_type *type = - glsl_type::get_array_instance(var->type->fields.array, - var->max_array_access + 1); - - assert(type != NULL); - var->type = type; - } - - return visit_continue; - } - } v; - - v.run(linked->ir); - } - - return linked; -} - - -struct uniform_node { - exec_node link; - struct gl_uniform *u; - unsigned slots; -}; - -/** - * Update the sizes of linked shader uniform arrays to the maximum - * array index used. - * - * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec: - * - * If one or more elements of an array are active, - * GetActiveUniform will return the name of the array in name, - * subject to the restrictions listed above. The type of the array - * is returned in type. The size parameter contains the highest - * array element index used, plus one. The compiler or linker - * determines the highest index used. There will be only one - * active uniform reported by the GL per uniform array. - - */ -static void -update_array_sizes(struct gl_shader_program *prog) -{ - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - foreach_list(node, prog->_LinkedShaders[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_uniform && - var->mode != ir_var_in && - var->mode != ir_var_out) || - !var->type->is_array()) - continue; - - unsigned int size = var->max_array_access; - for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) { - if (prog->_LinkedShaders[j] == NULL) - continue; - - foreach_list(node2, prog->_LinkedShaders[j]->ir) { - ir_variable *other_var = ((ir_instruction *) node2)->as_variable(); - if (!other_var) - continue; - - if (strcmp(var->name, other_var->name) == 0 && - other_var->max_array_access > size) { - size = other_var->max_array_access; - } - } - } - - if (size + 1 != var->type->fields.array->length) { - var->type = glsl_type::get_array_instance(var->type->fields.array, - size + 1); - /* FINISHME: We should update the types of array - * dereferences of this variable now. - */ - } - } - } -} - -static void -add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht, - const char *name, const glsl_type *type, GLenum shader_type, - unsigned *next_shader_pos, unsigned *total_uniforms) -{ - if (type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - const glsl_type *field_type = type->fields.structure[i].type; - char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name, - type->fields.structure[i].name); - - add_uniform(mem_ctx, uniforms, ht, field_name, field_type, - shader_type, next_shader_pos, total_uniforms); - } - } else { - uniform_node *n = (uniform_node *) hash_table_find(ht, name); - unsigned int vec4_slots; - const glsl_type *array_elem_type = NULL; - - if (type->is_array()) { - array_elem_type = type->fields.array; - /* Array of structures. */ - if (array_elem_type->is_record()) { - for (unsigned int i = 0; i < type->length; i++) { - char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i); - add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type, - shader_type, next_shader_pos, total_uniforms); - } - return; - } - } - - /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out - * vectors to vec4 slots. - */ - if (type->is_array()) { - if (array_elem_type->is_sampler()) - vec4_slots = type->length; - else - vec4_slots = type->length * array_elem_type->matrix_columns; - } else if (type->is_sampler()) { - vec4_slots = 1; - } else { - vec4_slots = type->matrix_columns; - } - - if (n == NULL) { - n = (uniform_node *) calloc(1, sizeof(struct uniform_node)); - n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform)); - n->slots = vec4_slots; - - n->u->Name = strdup(name); - n->u->Type = type; - n->u->VertPos = -1; - n->u->FragPos = -1; - n->u->GeomPos = -1; - (*total_uniforms)++; - - hash_table_insert(ht, n, name); - uniforms->push_tail(& n->link); - } - - switch (shader_type) { - case GL_VERTEX_SHADER: - n->u->VertPos = *next_shader_pos; - break; - case GL_FRAGMENT_SHADER: - n->u->FragPos = *next_shader_pos; - break; - case GL_GEOMETRY_SHADER: - n->u->GeomPos = *next_shader_pos; - break; - } - - (*next_shader_pos) += vec4_slots; - } -} - -void -assign_uniform_locations(struct gl_shader_program *prog) -{ - /* */ - exec_list uniforms; - unsigned total_uniforms = 0; - hash_table *ht = hash_table_ctor(32, hash_table_string_hash, - hash_table_string_compare); - void *mem_ctx = ralloc_context(NULL); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - unsigned next_position = 0; - - foreach_list(node, prog->_LinkedShaders[i]->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_uniform)) - continue; - - if (strncmp(var->name, "gl_", 3) == 0) { - /* At the moment, we don't allocate uniform locations for - * builtin uniforms. It's permitted by spec, and we'll - * likely switch to doing that at some point, but not yet. - */ - continue; - } - - var->location = next_position; - add_uniform(mem_ctx, &uniforms, ht, var->name, var->type, - prog->_LinkedShaders[i]->Type, - &next_position, &total_uniforms); - } - } - - ralloc_free(mem_ctx); - - gl_uniform_list *ul = (gl_uniform_list *) - calloc(1, sizeof(gl_uniform_list)); - - ul->Size = total_uniforms; - ul->NumUniforms = total_uniforms; - ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform)); - - unsigned idx = 0; - uniform_node *next; - for (uniform_node *node = (uniform_node *) uniforms.head - ; node->link.next != NULL - ; node = next) { - next = (uniform_node *) node->link.next; - - node->link.remove(); - memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform)); - idx++; - - free(node->u); - free(node); - } - - hash_table_dtor(ht); - - prog->Uniforms = ul; -} - - -/** - * Find a contiguous set of available bits in a bitmask - * - * \param used_mask Bits representing used (1) and unused (0) locations - * \param needed_count Number of contiguous bits needed. - * - * \return - * Base location of the available bits on success or -1 on failure. - */ -int -find_available_slots(unsigned used_mask, unsigned needed_count) -{ - unsigned needed_mask = (1 << needed_count) - 1; - const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count; - - /* The comparison to 32 is redundant, but without it GCC emits "warning: - * cannot optimize possibly infinite loops" for the loop below. - */ - if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32)) - return -1; - - for (int i = 0; i <= max_bit_to_test; i++) { - if ((needed_mask & ~used_mask) == needed_mask) - return i; - - needed_mask <<= 1; - } - - return -1; -} - - -bool -assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index) -{ - /* Mark invalid attribute locations as being used. - */ - unsigned used_locations = (max_attribute_index >= 32) - ? ~0 : ~((1 << max_attribute_index) - 1); - - gl_shader *const sh = prog->_LinkedShaders[0]; - assert(sh->Type == GL_VERTEX_SHADER); - - /* Operate in a total of four passes. - * - * 1. Invalidate the location assignments for all vertex shader inputs. - * - * 2. Assign locations for inputs that have user-defined (via - * glBindVertexAttribLocation) locatoins. - * - * 3. Sort the attributes without assigned locations by number of slots - * required in decreasing order. Fragmentation caused by attribute - * locations assigned by the application may prevent large attributes - * from having enough contiguous space. - * - * 4. Assign locations to any inputs without assigned locations. - */ - - invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0); - - if (prog->Attributes != NULL) { - for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) { - ir_variable *const var = - sh->symbols->get_variable(prog->Attributes->Parameters[i].Name); - - /* Note: attributes that occupy multiple slots, such as arrays or - * matrices, may appear in the attrib array multiple times. - */ - if ((var == NULL) || (var->location != -1)) - continue; - - /* From page 61 of the OpenGL 4.0 spec: - * - * "LinkProgram will fail if the attribute bindings assigned by - * BindAttribLocation do not leave not enough space to assign a - * location for an active matrix attribute or an active attribute - * array, both of which require multiple contiguous generic - * attributes." - * - * Previous versions of the spec contain similar language but omit the - * bit about attribute arrays. - * - * Page 61 of the OpenGL 4.0 spec also says: - * - * "It is possible for an application to bind more than one - * attribute name to the same location. This is referred to as - * aliasing. This will only work if only one of the aliased - * attributes is active in the executable program, or if no path - * through the shader consumes more than one attribute of a set - * of attributes aliased to the same location. A link error can - * occur if the linker determines that every path through the - * shader consumes multiple aliased attributes, but - * implementations are not required to generate an error in this - * case." - * - * These two paragraphs are either somewhat contradictory, or I don't - * fully understand one or both of them. - */ - /* FINISHME: The code as currently written does not support attribute - * FINISHME: location aliasing (see comment above). - */ - const int attr = prog->Attributes->Parameters[i].StateIndexes[0]; - const unsigned slots = count_attribute_slots(var->type); - - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << slots) - 1; - - /* Generate a link error if the set of bits requested for this - * attribute overlaps any previously allocated bits. - */ - if ((~(use_mask << attr) & used_locations) != used_locations) { - linker_error_printf(prog, - "insufficient contiguous attribute locations " - "available for vertex shader input `%s'", - var->name); - return false; - } - - var->location = VERT_ATTRIB_GENERIC0 + attr; - used_locations |= (use_mask << attr); - } - } - - /* Temporary storage for the set of attributes that need locations assigned. - */ - struct temp_attr { - unsigned slots; - ir_variable *var; - - /* Used below in the call to qsort. */ - static int compare(const void *a, const void *b) - { - const temp_attr *const l = (const temp_attr *) a; - const temp_attr *const r = (const temp_attr *) b; - - /* Reversed because we want a descending order sort below. */ - return r->slots - l->slots; - } - } to_assign[16]; - - unsigned num_attr = 0; - - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_in)) - continue; - - if (var->explicit_location) { - const unsigned slots = count_attribute_slots(var->type); - const unsigned use_mask = (1 << slots) - 1; - const int attr = var->location - VERT_ATTRIB_GENERIC0; - - if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0)) - || (var->location < 0)) { - linker_error_printf(prog, - "invalid explicit location %d specified for " - "`%s'\n", - (var->location < 0) ? var->location : attr, - var->name); - return false; - } else if (var->location >= VERT_ATTRIB_GENERIC0) { - used_locations |= (use_mask << attr); - } - } - - /* The location was explicitly assigned, nothing to do here. - */ - if (var->location != -1) - continue; - - to_assign[num_attr].slots = count_attribute_slots(var->type); - to_assign[num_attr].var = var; - num_attr++; - } - - /* If all of the attributes were assigned locations by the application (or - * are built-in attributes with fixed locations), return early. This should - * be the common case. - */ - if (num_attr == 0) - return true; - - qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare); - - /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS. It can only - * be explicitly assigned by via glBindAttribLocation. Mark it as reserved - * to prevent it from being automatically allocated below. - */ - find_deref_visitor find("gl_Vertex"); - find.run(sh->ir); - if (find.variable_found()) - used_locations |= (1 << 0); - - for (unsigned i = 0; i < num_attr; i++) { - /* Mask representing the contiguous slots that will be used by this - * attribute. - */ - const unsigned use_mask = (1 << to_assign[i].slots) - 1; - - int location = find_available_slots(used_locations, to_assign[i].slots); - - if (location < 0) { - linker_error_printf(prog, - "insufficient contiguous attribute locations " - "available for vertex shader input `%s'", - to_assign[i].var->name); - return false; - } - - to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location; - used_locations |= (use_mask << location); - } - - return true; -} - - -/** - * Demote shader inputs and outputs that are not used in other stages - */ -void -demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode) -{ - foreach_list(node, sh->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != int(mode))) - continue; - - /* A shader 'in' or 'out' variable is only really an input or output if - * its value is used by other shader stages. This will cause the variable - * to have a location assigned. - */ - if (var->location == -1) { - var->mode = ir_var_auto; - } - } -} - - -void -assign_varying_locations(struct gl_shader_program *prog, - gl_shader *producer, gl_shader *consumer) -{ - /* FINISHME: Set dynamically when geometry shader support is added. */ - unsigned output_index = VERT_RESULT_VAR0; - unsigned input_index = FRAG_ATTRIB_VAR0; - - /* Operate in a total of three passes. - * - * 1. Assign locations for any matching inputs and outputs. - * - * 2. Mark output variables in the producer that do not have locations as - * not being outputs. This lets the optimizer eliminate them. - * - * 3. Mark input variables in the consumer that do not have locations as - * not being inputs. This lets the optimizer eliminate them. - */ - - invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0); - invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0); - - foreach_list(node, producer->ir) { - ir_variable *const output_var = ((ir_instruction *) node)->as_variable(); - - if ((output_var == NULL) || (output_var->mode != ir_var_out) - || (output_var->location != -1)) - continue; - - ir_variable *const input_var = - consumer->symbols->get_variable(output_var->name); - - if ((input_var == NULL) || (input_var->mode != ir_var_in)) - continue; - - assert(input_var->location == -1); - - output_var->location = output_index; - input_var->location = input_index; - - /* FINISHME: Support for "varying" records in GLSL 1.50. */ - assert(!output_var->type->is_record()); - - if (output_var->type->is_array()) { - const unsigned slots = output_var->type->length - * output_var->type->fields.array->matrix_columns; - - output_index += slots; - input_index += slots; - } else { - const unsigned slots = output_var->type->matrix_columns; - - output_index += slots; - input_index += slots; - } - } - - foreach_list(node, consumer->ir) { - ir_variable *const var = ((ir_instruction *) node)->as_variable(); - - if ((var == NULL) || (var->mode != ir_var_in)) - continue; - - if (var->location == -1) { - if (prog->Version <= 120) { - /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec: - * - * Only those varying variables used (i.e. read) in - * the fragment shader executable must be written to - * by the vertex shader executable; declaring - * superfluous varying variables in a vertex shader is - * permissible. - * - * We interpret this text as meaning that the VS must - * write the variable for the FS to read it. See - * "glsl1-varying read but not written" in piglit. - */ - - linker_error_printf(prog, "fragment shader varying %s not written " - "by vertex shader\n.", var->name); - prog->LinkStatus = false; - } - - /* An 'in' variable is only really a shader input if its - * value is written by the previous stage. - */ - var->mode = ir_var_auto; - } - } -} - - -void -link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) -{ - void *mem_ctx = ralloc_context(NULL); // temporary linker context - - prog->LinkStatus = false; - prog->Validated = false; - prog->_Used = false; - - if (prog->InfoLog != NULL) - ralloc_free(prog->InfoLog); - - prog->InfoLog = ralloc_strdup(NULL, ""); - - /* Separate the shaders into groups based on their type. - */ - struct gl_shader **vert_shader_list; - unsigned num_vert_shaders = 0; - struct gl_shader **frag_shader_list; - unsigned num_frag_shaders = 0; - - vert_shader_list = (struct gl_shader **) - calloc(2 * prog->NumShaders, sizeof(struct gl_shader *)); - frag_shader_list = &vert_shader_list[prog->NumShaders]; - - unsigned min_version = UINT_MAX; - unsigned max_version = 0; - for (unsigned i = 0; i < prog->NumShaders; i++) { - min_version = MIN2(min_version, prog->Shaders[i]->Version); - max_version = MAX2(max_version, prog->Shaders[i]->Version); - - switch (prog->Shaders[i]->Type) { - case GL_VERTEX_SHADER: - vert_shader_list[num_vert_shaders] = prog->Shaders[i]; - num_vert_shaders++; - break; - case GL_FRAGMENT_SHADER: - frag_shader_list[num_frag_shaders] = prog->Shaders[i]; - num_frag_shaders++; - break; - case GL_GEOMETRY_SHADER: - /* FINISHME: Support geometry shaders. */ - assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER); - break; - } - } - - /* Previous to GLSL version 1.30, different compilation units could mix and - * match shading language versions. With GLSL 1.30 and later, the versions - * of all shaders must match. - */ - assert(min_version >= 100); - assert(max_version <= 130); - if ((max_version >= 130 || min_version == 100) - && min_version != max_version) { - linker_error_printf(prog, "all shaders must use same shading " - "language version\n"); - goto done; - } - - prog->Version = max_version; - - for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] != NULL) - ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]); - - prog->_LinkedShaders[i] = NULL; - } - - /* Link all shaders for a particular stage and validate the result. - */ - if (num_vert_shaders > 0) { - gl_shader *const sh = - link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list, - num_vert_shaders); - - if (sh == NULL) - goto done; - - if (!validate_vertex_shader_executable(prog, sh)) - goto done; - - _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX], - sh); - } - - if (num_frag_shaders > 0) { - gl_shader *const sh = - link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list, - num_frag_shaders); - - if (sh == NULL) - goto done; - - if (!validate_fragment_shader_executable(prog, sh)) - goto done; - - _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT], - sh); - } - - /* Here begins the inter-stage linking phase. Some initial validation is - * performed, then locations are assigned for uniforms, attributes, and - * varyings. - */ - if (cross_validate_uniforms(prog)) { - unsigned prev; - - for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { - if (prog->_LinkedShaders[prev] != NULL) - break; - } - - /* Validate the inputs of each stage with the output of the preceeding - * stage. - */ - for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - if (!cross_validate_outputs_to_inputs(prog, - prog->_LinkedShaders[prev], - prog->_LinkedShaders[i])) - goto done; - - prev = i; - } - - prog->LinkStatus = true; - } - - /* Do common optimization before assigning storage for attributes, - * uniforms, and varyings. Later optimization could possibly make - * some of that unused. - */ - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) - ; - } - - update_array_sizes(prog); - - assign_uniform_locations(prog); - - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { - /* FINISHME: The value of the max_attribute_index parameter is - * FINISHME: implementation dependent based on the value of - * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be - * FINISHME: at least 16, so hardcode 16 for now. - */ - if (!assign_attribute_locations(prog, 16)) { - prog->LinkStatus = false; - goto done; - } - } - - unsigned prev; - for (prev = 0; prev < MESA_SHADER_TYPES; prev++) { - if (prog->_LinkedShaders[prev] != NULL) - break; - } - - for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - assign_varying_locations(prog, - prog->_LinkedShaders[prev], - prog->_LinkedShaders[i]); - prev = i; - } - - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) { - demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX], - ir_var_out); - } - - if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY]; - - demote_shader_inputs_and_outputs(sh, ir_var_in); - demote_shader_inputs_and_outputs(sh, ir_var_inout); - demote_shader_inputs_and_outputs(sh, ir_var_out); - } - - if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) { - gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT]; - - demote_shader_inputs_and_outputs(sh, ir_var_in); - } - - /* OpenGL ES requires that a vertex shader and a fragment shader both be - * present in a linked program. By checking for use of shading language - * version 1.00, we also catch the GL_ARB_ES2_compatibility case. - */ - if (ctx->API == API_OPENGLES2 || prog->Version == 100) { - if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) { - linker_error_printf(prog, "program lacks a vertex shader\n"); - prog->LinkStatus = false; - } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { - linker_error_printf(prog, "program lacks a fragment shader\n"); - prog->LinkStatus = false; - } - } - - /* FINISHME: Assign fragment shader output locations. */ - -done: - free(vert_shader_list); - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { - if (prog->_LinkedShaders[i] == NULL) - continue; - - /* Retain any live IR, but trash the rest. */ - reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir); - } - - ralloc_free(mem_ctx); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file linker.cpp
+ * GLSL linker implementation
+ *
+ * Given a set of shaders that are to be linked to generate a final program,
+ * there are three distinct stages.
+ *
+ * In the first stage shaders are partitioned into groups based on the shader
+ * type. All shaders of a particular type (e.g., vertex shaders) are linked
+ * together.
+ *
+ * - Undefined references in each shader are resolve to definitions in
+ * another shader.
+ * - Types and qualifiers of uniforms, outputs, and global variables defined
+ * in multiple shaders with the same name are verified to be the same.
+ * - Initializers for uniforms and global variables defined
+ * in multiple shaders with the same name are verified to be the same.
+ *
+ * The result, in the terminology of the GLSL spec, is a set of shader
+ * executables for each processing unit.
+ *
+ * After the first stage is complete, a series of semantic checks are performed
+ * on each of the shader executables.
+ *
+ * - Each shader executable must define a \c main function.
+ * - Each vertex shader executable must write to \c gl_Position.
+ * - Each fragment shader executable must write to either \c gl_FragData or
+ * \c gl_FragColor.
+ *
+ * In the final stage individual shader executables are linked to create a
+ * complete exectuable.
+ *
+ * - Types of uniforms defined in multiple shader stages with the same name
+ * are verified to be the same.
+ * - Initializers for uniforms defined in multiple shader stages with the
+ * same name are verified to be the same.
+ * - Types and qualifiers of outputs defined in one stage are verified to
+ * be the same as the types and qualifiers of inputs defined with the same
+ * name in a later stage.
+ *
+ * \author Ian Romanick <ian.d.romanick@intel.com>
+ */
+
+#include "main/core.h"
+#include "glsl_symbol_table.h"
+#include "ir.h"
+#include "program.h"
+#include "program/hash_table.h"
+#include "linker.h"
+#include "ir_optimization.h"
+
+extern "C" {
+#include "main/shaderobj.h"
+}
+
+/**
+ * Visitor that determines whether or not a variable is ever written.
+ */
+class find_assignment_visitor : public ir_hierarchical_visitor {
+public:
+ find_assignment_visitor(const char *name)
+ : name(name), found(false)
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_assignment *ir)
+ {
+ ir_variable *const var = ir->lhs->variable_referenced();
+
+ if (strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+
+ return visit_continue_with_parent;
+ }
+
+ virtual ir_visitor_status visit_enter(ir_call *ir)
+ {
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *sig_param = (ir_variable *)sig_iter.get();
+
+ if (sig_param->mode == ir_var_out ||
+ sig_param->mode == ir_var_inout) {
+ ir_variable *var = param_rval->variable_referenced();
+ if (var && strcmp(name, var->name) == 0) {
+ found = true;
+ return visit_stop;
+ }
+ }
+ sig_iter.next();
+ }
+
+ return visit_continue_with_parent;
+ }
+
+ bool variable_found()
+ {
+ return found;
+ }
+
+private:
+ const char *name; /**< Find writes to a variable with this name. */
+ bool found; /**< Was a write to the variable found? */
+};
+
+
+/**
+ * Visitor that determines whether or not a variable is ever read.
+ */
+class find_deref_visitor : public ir_hierarchical_visitor {
+public:
+ find_deref_visitor(const char *name)
+ : name(name), found(false)
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ if (strcmp(this->name, ir->var->name) == 0) {
+ this->found = true;
+ return visit_stop;
+ }
+
+ return visit_continue;
+ }
+
+ bool variable_found() const
+ {
+ return this->found;
+ }
+
+private:
+ const char *name; /**< Find writes to a variable with this name. */
+ bool found; /**< Was a write to the variable found? */
+};
+
+
+void
+linker_error_printf(gl_shader_program *prog, const char *fmt, ...)
+{
+ va_list ap;
+
+ ralloc_strcat(&prog->InfoLog, "error: ");
+ va_start(ap, fmt);
+ ralloc_vasprintf_append(&prog->InfoLog, fmt, ap);
+ va_end(ap);
+}
+
+
+void
+invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
+ int generic_base)
+{
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != (unsigned) mode))
+ continue;
+
+ /* Only assign locations for generic attributes / varyings / etc.
+ */
+ if ((var->location >= generic_base) && !var->explicit_location)
+ var->location = -1;
+ }
+}
+
+
+/**
+ * Determine the number of attribute slots required for a particular type
+ *
+ * This code is here because it implements the language rules of a specific
+ * GLSL version. Since it's a property of the language and not a property of
+ * types in general, it doesn't really belong in glsl_type.
+ */
+unsigned
+count_attribute_slots(const glsl_type *t)
+{
+ /* From page 31 (page 37 of the PDF) of the GLSL 1.50 spec:
+ *
+ * "A scalar input counts the same amount against this limit as a vec4,
+ * so applications may want to consider packing groups of four
+ * unrelated float inputs together into a vector to better utilize the
+ * capabilities of the underlying hardware. A matrix input will use up
+ * multiple locations. The number of locations used will equal the
+ * number of columns in the matrix."
+ *
+ * The spec does not explicitly say how arrays are counted. However, it
+ * should be safe to assume the total number of slots consumed by an array
+ * is the number of entries in the array multiplied by the number of slots
+ * consumed by a single element of the array.
+ */
+
+ if (t->is_array())
+ return t->array_size() * count_attribute_slots(t->element_type());
+
+ if (t->is_matrix())
+ return t->matrix_columns;
+
+ return 1;
+}
+
+
+/**
+ * Verify that a vertex shader executable meets all semantic requirements
+ *
+ * \param shader Vertex shader executable to be verified
+ */
+bool
+validate_vertex_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
+{
+ if (shader == NULL)
+ return true;
+
+ find_assignment_visitor find("gl_Position");
+ find.run(shader->ir);
+ if (!find.variable_found()) {
+ linker_error_printf(prog,
+ "vertex shader does not write to `gl_Position'\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Verify that a fragment shader executable meets all semantic requirements
+ *
+ * \param shader Fragment shader executable to be verified
+ */
+bool
+validate_fragment_shader_executable(struct gl_shader_program *prog,
+ struct gl_shader *shader)
+{
+ if (shader == NULL)
+ return true;
+
+ find_assignment_visitor frag_color("gl_FragColor");
+ find_assignment_visitor frag_data("gl_FragData");
+
+ frag_color.run(shader->ir);
+ frag_data.run(shader->ir);
+
+ if (frag_color.variable_found() && frag_data.variable_found()) {
+ linker_error_printf(prog, "fragment shader writes to both "
+ "`gl_FragColor' and `gl_FragData'\n");
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Generate a string describing the mode of a variable
+ */
+static const char *
+mode_string(const ir_variable *var)
+{
+ switch (var->mode) {
+ case ir_var_auto:
+ return (var->read_only) ? "global constant" : "global variable";
+
+ case ir_var_uniform: return "uniform";
+ case ir_var_in: return "shader input";
+ case ir_var_out: return "shader output";
+ case ir_var_inout: return "shader inout";
+
+ case ir_var_const_in:
+ case ir_var_temporary:
+ default:
+ assert(!"Should not get here.");
+ return "invalid variable";
+ }
+}
+
+
+/**
+ * Perform validation of global variables used across multiple shaders
+ */
+bool
+cross_validate_globals(struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders,
+ bool uniforms_only)
+{
+ /* Examine all of the uniforms in all of the shaders and cross validate
+ * them.
+ */
+ glsl_symbol_table variables;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (shader_list[i] == NULL)
+ continue;
+
+ foreach_list(node, shader_list[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if (var == NULL)
+ continue;
+
+ if (uniforms_only && (var->mode != ir_var_uniform))
+ continue;
+
+ /* Don't cross validate temporaries that are at global scope. These
+ * will eventually get pulled into the shaders 'main'.
+ */
+ if (var->mode == ir_var_temporary)
+ continue;
+
+ /* If a global with this name has already been seen, verify that the
+ * new instance has the same type. In addition, if the globals have
+ * initializers, the values of the initializers must be the same.
+ */
+ ir_variable *const existing = variables.get_variable(var->name);
+ if (existing != NULL) {
+ if (var->type != existing->type) {
+ /* Consider the types to be "the same" if both types are arrays
+ * of the same type and one of the arrays is implicitly sized.
+ * In addition, set the type of the linked variable to the
+ * explicitly sized array.
+ */
+ if (var->type->is_array()
+ && existing->type->is_array()
+ && (var->type->fields.array == existing->type->fields.array)
+ && ((var->type->length == 0)
+ || (existing->type->length == 0))) {
+ if (var->type->length != 0) {
+ existing->type = var->type;
+ }
+ } else {
+ linker_error_printf(prog, "%s `%s' declared as type "
+ "`%s' and type `%s'\n",
+ mode_string(var),
+ var->name, var->type->name,
+ existing->type->name);
+ return false;
+ }
+ }
+
+ if (var->explicit_location) {
+ if (existing->explicit_location
+ && (var->location != existing->location)) {
+ linker_error_printf(prog, "explicit locations for %s "
+ "`%s' have differing values\n",
+ mode_string(var), var->name);
+ return false;
+ }
+
+ existing->location = var->location;
+ existing->explicit_location = true;
+ }
+
+ /* Validate layout qualifiers for gl_FragDepth.
+ *
+ * From the AMD_conservative_depth spec:
+ * "If gl_FragDepth is redeclared in any fragment shader in
+ * a program, it must be redeclared in all fragment shaders in that
+ * program that have static assignments to gl_FragDepth. All
+ * redeclarations of gl_FragDepth in all fragment shaders in
+ * a single program must have the same set of qualifiers."
+ */
+ if (strcmp(var->name, "gl_FragDepth") == 0) {
+ bool layout_declared = var->depth_layout != ir_depth_layout_none;
+ bool layout_differs = var->depth_layout != existing->depth_layout;
+ if (layout_declared && layout_differs) {
+ linker_error_printf(prog,
+ "All redeclarations of gl_FragDepth in all fragment shaders "
+ "in a single program must have the same set of qualifiers.");
+ }
+ if (var->used && layout_differs) {
+ linker_error_printf(prog,
+ "If gl_FragDepth is redeclared with a layout qualifier in"
+ "any fragment shader, it must be redeclared with the same"
+ "layout qualifier in all fragment shaders that have"
+ "assignments to gl_FragDepth");
+ }
+ }
+
+ /* FINISHME: Handle non-constant initializers.
+ */
+ if (var->constant_value != NULL) {
+ if (existing->constant_value != NULL) {
+ if (!var->constant_value->has_value(existing->constant_value)) {
+ linker_error_printf(prog, "initializers for %s "
+ "`%s' have differing values\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ } else
+ /* If the first-seen instance of a particular uniform did not
+ * have an initializer but a later instance does, copy the
+ * initializer to the version stored in the symbol table.
+ */
+ /* FINISHME: This is wrong. The constant_value field should
+ * FINISHME: not be modified! Imagine a case where a shader
+ * FINISHME: without an initializer is linked in two different
+ * FINISHME: programs with shaders that have differing
+ * FINISHME: initializers. Linking with the first will
+ * FINISHME: modify the shader, and linking with the second
+ * FINISHME: will fail.
+ */
+ existing->constant_value =
+ var->constant_value->clone(ralloc_parent(existing), NULL);
+ }
+
+ if (existing->invariant != var->invariant) {
+ linker_error_printf(prog, "declarations for %s `%s' have "
+ "mismatching invariant qualifiers\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ if (existing->centroid != var->centroid) {
+ linker_error_printf(prog, "declarations for %s `%s' have "
+ "mismatching centroid qualifiers\n",
+ mode_string(var), var->name);
+ return false;
+ }
+ } else
+ variables.add_variable(var);
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Perform validation of uniforms used across multiple shader stages
+ */
+bool
+cross_validate_uniforms(struct gl_shader_program *prog)
+{
+ return cross_validate_globals(prog, prog->_LinkedShaders,
+ MESA_SHADER_TYPES, true);
+}
+
+
+/**
+ * Validate that outputs from one stage match inputs of another
+ */
+bool
+cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
+ gl_shader *producer, gl_shader *consumer)
+{
+ glsl_symbol_table parameters;
+ /* FINISHME: Figure these out dynamically. */
+ const char *const producer_stage = "vertex";
+ const char *const consumer_stage = "fragment";
+
+ /* Find all shader outputs in the "producer" stage.
+ */
+ foreach_list(node, producer->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ /* FINISHME: For geometry shaders, this should also look for inout
+ * FINISHME: variables.
+ */
+ if ((var == NULL) || (var->mode != ir_var_out))
+ continue;
+
+ parameters.add_variable(var);
+ }
+
+
+ /* Find all shader inputs in the "consumer" stage. Any variables that have
+ * matching outputs already in the symbol table must have the same type and
+ * qualifiers.
+ */
+ foreach_list(node, consumer->ir) {
+ ir_variable *const input = ((ir_instruction *) node)->as_variable();
+
+ /* FINISHME: For geometry shaders, this should also look for inout
+ * FINISHME: variables.
+ */
+ if ((input == NULL) || (input->mode != ir_var_in))
+ continue;
+
+ ir_variable *const output = parameters.get_variable(input->name);
+ if (output != NULL) {
+ /* Check that the types match between stages.
+ */
+ if (input->type != output->type) {
+ /* There is a bit of a special case for gl_TexCoord. This
+ * built-in is unsized by default. Appliations that variable
+ * access it must redeclare it with a size. There is some
+ * language in the GLSL spec that implies the fragment shader
+ * and vertex shader do not have to agree on this size. Other
+ * driver behave this way, and one or two applications seem to
+ * rely on it.
+ *
+ * Neither declaration needs to be modified here because the array
+ * sizes are fixed later when update_array_sizes is called.
+ *
+ * From page 48 (page 54 of the PDF) of the GLSL 1.10 spec:
+ *
+ * "Unlike user-defined varying variables, the built-in
+ * varying variables don't have a strict one-to-one
+ * correspondence between the vertex language and the
+ * fragment language."
+ */
+ if (!output->type->is_array()
+ || (strncmp("gl_", output->name, 3) != 0)) {
+ linker_error_printf(prog,
+ "%s shader output `%s' declared as "
+ "type `%s', but %s shader input declared "
+ "as type `%s'\n",
+ producer_stage, output->name,
+ output->type->name,
+ consumer_stage, input->type->name);
+ return false;
+ }
+ }
+
+ /* Check that all of the qualifiers match between stages.
+ */
+ if (input->centroid != output->centroid) {
+ linker_error_printf(prog,
+ "%s shader output `%s' %s centroid qualifier, "
+ "but %s shader input %s centroid qualifier\n",
+ producer_stage,
+ output->name,
+ (output->centroid) ? "has" : "lacks",
+ consumer_stage,
+ (input->centroid) ? "has" : "lacks");
+ return false;
+ }
+
+ if (input->invariant != output->invariant) {
+ linker_error_printf(prog,
+ "%s shader output `%s' %s invariant qualifier, "
+ "but %s shader input %s invariant qualifier\n",
+ producer_stage,
+ output->name,
+ (output->invariant) ? "has" : "lacks",
+ consumer_stage,
+ (input->invariant) ? "has" : "lacks");
+ return false;
+ }
+
+ if (input->interpolation != output->interpolation) {
+ linker_error_printf(prog,
+ "%s shader output `%s' specifies %s "
+ "interpolation qualifier, "
+ "but %s shader input specifies %s "
+ "interpolation qualifier\n",
+ producer_stage,
+ output->name,
+ output->interpolation_string(),
+ consumer_stage,
+ input->interpolation_string());
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Populates a shaders symbol table with all global declarations
+ */
+static void
+populate_symbol_table(gl_shader *sh)
+{
+ sh->symbols = new(sh) glsl_symbol_table;
+
+ foreach_list(node, sh->ir) {
+ ir_instruction *const inst = (ir_instruction *) node;
+ ir_variable *var;
+ ir_function *func;
+
+ if ((func = inst->as_function()) != NULL) {
+ sh->symbols->add_function(func);
+ } else if ((var = inst->as_variable()) != NULL) {
+ sh->symbols->add_variable(var);
+ }
+ }
+}
+
+
+/**
+ * Remap variables referenced in an instruction tree
+ *
+ * This is used when instruction trees are cloned from one shader and placed in
+ * another. These trees will contain references to \c ir_variable nodes that
+ * do not exist in the target shader. This function finds these \c ir_variable
+ * references and replaces the references with matching variables in the target
+ * shader.
+ *
+ * If there is no matching variable in the target shader, a clone of the
+ * \c ir_variable is made and added to the target shader. The new variable is
+ * added to \b both the instruction stream and the symbol table.
+ *
+ * \param inst IR tree that is to be processed.
+ * \param symbols Symbol table containing global scope symbols in the
+ * linked shader.
+ * \param instructions Instruction stream where new variable declarations
+ * should be added.
+ */
+void
+remap_variables(ir_instruction *inst, struct gl_shader *target,
+ hash_table *temps)
+{
+ class remap_visitor : public ir_hierarchical_visitor {
+ public:
+ remap_visitor(struct gl_shader *target,
+ hash_table *temps)
+ {
+ this->target = target;
+ this->symbols = target->symbols;
+ this->instructions = target->ir;
+ this->temps = temps;
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ if (ir->var->mode == ir_var_temporary) {
+ ir_variable *var = (ir_variable *) hash_table_find(temps, ir->var);
+
+ assert(var != NULL);
+ ir->var = var;
+ return visit_continue;
+ }
+
+ ir_variable *const existing =
+ this->symbols->get_variable(ir->var->name);
+ if (existing != NULL)
+ ir->var = existing;
+ else {
+ ir_variable *copy = ir->var->clone(this->target, NULL);
+
+ this->symbols->add_variable(copy);
+ this->instructions->push_head(copy);
+ ir->var = copy;
+ }
+
+ return visit_continue;
+ }
+
+ private:
+ struct gl_shader *target;
+ glsl_symbol_table *symbols;
+ exec_list *instructions;
+ hash_table *temps;
+ };
+
+ remap_visitor v(target, temps);
+
+ inst->accept(&v);
+}
+
+
+/**
+ * Move non-declarations from one instruction stream to another
+ *
+ * The intended usage pattern of this function is to pass the pointer to the
+ * head sentinel of a list (i.e., a pointer to the list cast to an \c exec_node
+ * pointer) for \c last and \c false for \c make_copies on the first
+ * call. Successive calls pass the return value of the previous call for
+ * \c last and \c true for \c make_copies.
+ *
+ * \param instructions Source instruction stream
+ * \param last Instruction after which new instructions should be
+ * inserted in the target instruction stream
+ * \param make_copies Flag selecting whether instructions in \c instructions
+ * should be copied (via \c ir_instruction::clone) into the
+ * target list or moved.
+ *
+ * \return
+ * The new "last" instruction in the target instruction stream. This pointer
+ * is suitable for use as the \c last parameter of a later call to this
+ * function.
+ */
+exec_node *
+move_non_declarations(exec_list *instructions, exec_node *last,
+ bool make_copies, gl_shader *target)
+{
+ hash_table *temps = NULL;
+
+ if (make_copies)
+ temps = hash_table_ctor(0, hash_table_pointer_hash,
+ hash_table_pointer_compare);
+
+ foreach_list_safe(node, instructions) {
+ ir_instruction *inst = (ir_instruction *) node;
+
+ if (inst->as_function())
+ continue;
+
+ ir_variable *var = inst->as_variable();
+ if ((var != NULL) && (var->mode != ir_var_temporary))
+ continue;
+
+ assert(inst->as_assignment()
+ || ((var != NULL) && (var->mode == ir_var_temporary)));
+
+ if (make_copies) {
+ inst = inst->clone(target, NULL);
+
+ if (var != NULL)
+ hash_table_insert(temps, inst, var);
+ else
+ remap_variables(inst, target, temps);
+ } else {
+ inst->remove();
+ }
+
+ last->insert_after(inst);
+ last = inst;
+ }
+
+ if (make_copies)
+ hash_table_dtor(temps);
+
+ return last;
+}
+
+/**
+ * Get the function signature for main from a shader
+ */
+static ir_function_signature *
+get_main_function_signature(gl_shader *sh)
+{
+ ir_function *const f = sh->symbols->get_function("main");
+ if (f != NULL) {
+ exec_list void_parameters;
+
+ /* Look for the 'void main()' signature and ensure that it's defined.
+ * This keeps the linker from accidentally pick a shader that just
+ * contains a prototype for main.
+ *
+ * We don't have to check for multiple definitions of main (in multiple
+ * shaders) because that would have already been caught above.
+ */
+ ir_function_signature *sig = f->matching_signature(&void_parameters);
+ if ((sig != NULL) && sig->is_defined) {
+ return sig;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Combine a group of shaders for a single stage to generate a linked shader
+ *
+ * \note
+ * If this function is supplied a single shader, it is cloned, and the new
+ * shader is returned.
+ */
+static struct gl_shader *
+link_intrastage_shaders(void *mem_ctx,
+ struct gl_context *ctx,
+ struct gl_shader_program *prog,
+ struct gl_shader **shader_list,
+ unsigned num_shaders)
+{
+ /* Check that global variables defined in multiple shaders are consistent.
+ */
+ if (!cross_validate_globals(prog, shader_list, num_shaders, false))
+ return NULL;
+
+ /* Check that there is only a single definition of each function signature
+ * across all shaders.
+ */
+ for (unsigned i = 0; i < (num_shaders - 1); i++) {
+ foreach_list(node, shader_list[i]->ir) {
+ ir_function *const f = ((ir_instruction *) node)->as_function();
+
+ if (f == NULL)
+ continue;
+
+ for (unsigned j = i + 1; j < num_shaders; j++) {
+ ir_function *const other =
+ shader_list[j]->symbols->get_function(f->name);
+
+ /* If the other shader has no function (and therefore no function
+ * signatures) with the same name, skip to the next shader.
+ */
+ if (other == NULL)
+ continue;
+
+ foreach_iter (exec_list_iterator, iter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) iter.get();
+
+ if (!sig->is_defined || sig->is_builtin)
+ continue;
+
+ ir_function_signature *other_sig =
+ other->exact_matching_signature(& sig->parameters);
+
+ if ((other_sig != NULL) && other_sig->is_defined
+ && !other_sig->is_builtin) {
+ linker_error_printf(prog,
+ "function `%s' is multiply defined",
+ f->name);
+ return NULL;
+ }
+ }
+ }
+ }
+ }
+
+ /* Find the shader that defines main, and make a clone of it.
+ *
+ * Starting with the clone, search for undefined references. If one is
+ * found, find the shader that defines it. Clone the reference and add
+ * it to the shader. Repeat until there are no undefined references or
+ * until a reference cannot be resolved.
+ */
+ gl_shader *main = NULL;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (get_main_function_signature(shader_list[i]) != NULL) {
+ main = shader_list[i];
+ break;
+ }
+ }
+
+ if (main == NULL) {
+ linker_error_printf(prog, "%s shader lacks `main'\n",
+ (shader_list[0]->Type == GL_VERTEX_SHADER)
+ ? "vertex" : "fragment");
+ return NULL;
+ }
+
+ gl_shader *linked = ctx->Driver.NewShader(NULL, 0, main->Type);
+ linked->ir = new(linked) exec_list;
+ clone_ir_list(mem_ctx, linked->ir, main->ir);
+
+ populate_symbol_table(linked);
+
+ /* The a pointer to the main function in the final linked shader (i.e., the
+ * copy of the original shader that contained the main function).
+ */
+ ir_function_signature *const main_sig = get_main_function_signature(linked);
+
+ /* Move any instructions other than variable declarations or function
+ * declarations into main.
+ */
+ exec_node *insertion_point =
+ move_non_declarations(linked->ir, (exec_node *) &main_sig->body, false,
+ linked);
+
+ for (unsigned i = 0; i < num_shaders; i++) {
+ if (shader_list[i] == main)
+ continue;
+
+ insertion_point = move_non_declarations(shader_list[i]->ir,
+ insertion_point, true, linked);
+ }
+
+ /* Resolve initializers for global variables in the linked shader.
+ */
+ unsigned num_linking_shaders = num_shaders;
+ for (unsigned i = 0; i < num_shaders; i++)
+ num_linking_shaders += shader_list[i]->num_builtins_to_link;
+
+ gl_shader **linking_shaders =
+ (gl_shader **) calloc(num_linking_shaders, sizeof(gl_shader *));
+
+ memcpy(linking_shaders, shader_list,
+ sizeof(linking_shaders[0]) * num_shaders);
+
+ unsigned idx = num_shaders;
+ for (unsigned i = 0; i < num_shaders; i++) {
+ memcpy(&linking_shaders[idx], shader_list[i]->builtins_to_link,
+ sizeof(linking_shaders[0]) * shader_list[i]->num_builtins_to_link);
+ idx += shader_list[i]->num_builtins_to_link;
+ }
+
+ assert(idx == num_linking_shaders);
+
+ if (!link_function_calls(prog, linked, linking_shaders,
+ num_linking_shaders)) {
+ ctx->Driver.DeleteShader(ctx, linked);
+ linked = NULL;
+ }
+
+ free(linking_shaders);
+
+ /* Make a pass over all variable declarations to ensure that arrays with
+ * unspecified sizes have a size specified. The size is inferred from the
+ * max_array_access field.
+ */
+ if (linked != NULL) {
+ class array_sizing_visitor : public ir_hierarchical_visitor {
+ public:
+ virtual ir_visitor_status visit(ir_variable *var)
+ {
+ if (var->type->is_array() && (var->type->length == 0)) {
+ const glsl_type *type =
+ glsl_type::get_array_instance(var->type->fields.array,
+ var->max_array_access + 1);
+
+ assert(type != NULL);
+ var->type = type;
+ }
+
+ return visit_continue;
+ }
+ } v;
+
+ v.run(linked->ir);
+ }
+
+ return linked;
+}
+
+
+struct uniform_node {
+ exec_node link;
+ struct gl_uniform *u;
+ unsigned slots;
+};
+
+/**
+ * Update the sizes of linked shader uniform arrays to the maximum
+ * array index used.
+ *
+ * From page 81 (page 95 of the PDF) of the OpenGL 2.1 spec:
+ *
+ * If one or more elements of an array are active,
+ * GetActiveUniform will return the name of the array in name,
+ * subject to the restrictions listed above. The type of the array
+ * is returned in type. The size parameter contains the highest
+ * array element index used, plus one. The compiler or linker
+ * determines the highest index used. There will be only one
+ * active uniform reported by the GL per uniform array.
+
+ */
+static void
+update_array_sizes(struct gl_shader_program *prog)
+{
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ foreach_list(node, prog->_LinkedShaders[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_uniform &&
+ var->mode != ir_var_in &&
+ var->mode != ir_var_out) ||
+ !var->type->is_array())
+ continue;
+
+ unsigned int size = var->max_array_access;
+ for (unsigned j = 0; j < MESA_SHADER_TYPES; j++) {
+ if (prog->_LinkedShaders[j] == NULL)
+ continue;
+
+ foreach_list(node2, prog->_LinkedShaders[j]->ir) {
+ ir_variable *other_var = ((ir_instruction *) node2)->as_variable();
+ if (!other_var)
+ continue;
+
+ if (strcmp(var->name, other_var->name) == 0 &&
+ other_var->max_array_access > size) {
+ size = other_var->max_array_access;
+ }
+ }
+ }
+
+ if (size + 1 != var->type->fields.array->length) {
+ var->type = glsl_type::get_array_instance(var->type->fields.array,
+ size + 1);
+ /* FINISHME: We should update the types of array
+ * dereferences of this variable now.
+ */
+ }
+ }
+ }
+}
+
+static void
+add_uniform(void *mem_ctx, exec_list *uniforms, struct hash_table *ht,
+ const char *name, const glsl_type *type, GLenum shader_type,
+ unsigned *next_shader_pos, unsigned *total_uniforms)
+{
+ if (type->is_record()) {
+ for (unsigned int i = 0; i < type->length; i++) {
+ const glsl_type *field_type = type->fields.structure[i].type;
+ char *field_name = ralloc_asprintf(mem_ctx, "%s.%s", name,
+ type->fields.structure[i].name);
+
+ add_uniform(mem_ctx, uniforms, ht, field_name, field_type,
+ shader_type, next_shader_pos, total_uniforms);
+ }
+ } else {
+ uniform_node *n = (uniform_node *) hash_table_find(ht, name);
+ unsigned int vec4_slots;
+ const glsl_type *array_elem_type = NULL;
+
+ if (type->is_array()) {
+ array_elem_type = type->fields.array;
+ /* Array of structures. */
+ if (array_elem_type->is_record()) {
+ for (unsigned int i = 0; i < type->length; i++) {
+ char *elem_name = ralloc_asprintf(mem_ctx, "%s[%d]", name, i);
+ add_uniform(mem_ctx, uniforms, ht, elem_name, array_elem_type,
+ shader_type, next_shader_pos, total_uniforms);
+ }
+ return;
+ }
+ }
+
+ /* Fix the storage size of samplers at 1 vec4 each. Be sure to pad out
+ * vectors to vec4 slots.
+ */
+ if (type->is_array()) {
+ if (array_elem_type->is_sampler())
+ vec4_slots = type->length;
+ else
+ vec4_slots = type->length * array_elem_type->matrix_columns;
+ } else if (type->is_sampler()) {
+ vec4_slots = 1;
+ } else {
+ vec4_slots = type->matrix_columns;
+ }
+
+ if (n == NULL) {
+ n = (uniform_node *) calloc(1, sizeof(struct uniform_node));
+ n->u = (gl_uniform *) calloc(1, sizeof(struct gl_uniform));
+ n->slots = vec4_slots;
+
+ n->u->Name = strdup(name);
+ n->u->Type = type;
+ n->u->VertPos = -1;
+ n->u->FragPos = -1;
+ n->u->GeomPos = -1;
+ (*total_uniforms)++;
+
+ hash_table_insert(ht, n, name);
+ uniforms->push_tail(& n->link);
+ }
+
+ switch (shader_type) {
+ case GL_VERTEX_SHADER:
+ n->u->VertPos = *next_shader_pos;
+ break;
+ case GL_FRAGMENT_SHADER:
+ n->u->FragPos = *next_shader_pos;
+ break;
+ case GL_GEOMETRY_SHADER:
+ n->u->GeomPos = *next_shader_pos;
+ break;
+ }
+
+ (*next_shader_pos) += vec4_slots;
+ }
+}
+
+void
+assign_uniform_locations(struct gl_shader_program *prog)
+{
+ /* */
+ exec_list uniforms;
+ unsigned total_uniforms = 0;
+ hash_table *ht = hash_table_ctor(32, hash_table_string_hash,
+ hash_table_string_compare);
+ void *mem_ctx = ralloc_context(NULL);
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ unsigned next_position = 0;
+
+ foreach_list(node, prog->_LinkedShaders[i]->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_uniform))
+ continue;
+
+ if (strncmp(var->name, "gl_", 3) == 0) {
+ /* At the moment, we don't allocate uniform locations for
+ * builtin uniforms. It's permitted by spec, and we'll
+ * likely switch to doing that at some point, but not yet.
+ */
+ continue;
+ }
+
+ var->location = next_position;
+ add_uniform(mem_ctx, &uniforms, ht, var->name, var->type,
+ prog->_LinkedShaders[i]->Type,
+ &next_position, &total_uniforms);
+ }
+ }
+
+ ralloc_free(mem_ctx);
+
+ gl_uniform_list *ul = (gl_uniform_list *)
+ calloc(1, sizeof(gl_uniform_list));
+
+ ul->Size = total_uniforms;
+ ul->NumUniforms = total_uniforms;
+ ul->Uniforms = (gl_uniform *) calloc(total_uniforms, sizeof(gl_uniform));
+
+ unsigned idx = 0;
+ uniform_node *next;
+ for (uniform_node *node = (uniform_node *) uniforms.head
+ ; node->link.next != NULL
+ ; node = next) {
+ next = (uniform_node *) node->link.next;
+
+ node->link.remove();
+ memcpy(&ul->Uniforms[idx], node->u, sizeof(gl_uniform));
+ idx++;
+
+ free(node->u);
+ free(node);
+ }
+
+ hash_table_dtor(ht);
+
+ prog->Uniforms = ul;
+}
+
+
+/**
+ * Find a contiguous set of available bits in a bitmask
+ *
+ * \param used_mask Bits representing used (1) and unused (0) locations
+ * \param needed_count Number of contiguous bits needed.
+ *
+ * \return
+ * Base location of the available bits on success or -1 on failure.
+ */
+int
+find_available_slots(unsigned used_mask, unsigned needed_count)
+{
+ unsigned needed_mask = (1 << needed_count) - 1;
+ const int max_bit_to_test = (8 * sizeof(used_mask)) - needed_count;
+
+ /* The comparison to 32 is redundant, but without it GCC emits "warning:
+ * cannot optimize possibly infinite loops" for the loop below.
+ */
+ if ((needed_count == 0) || (max_bit_to_test < 0) || (max_bit_to_test > 32))
+ return -1;
+
+ for (int i = 0; i <= max_bit_to_test; i++) {
+ if ((needed_mask & ~used_mask) == needed_mask)
+ return i;
+
+ needed_mask <<= 1;
+ }
+
+ return -1;
+}
+
+
+bool
+assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index)
+{
+ /* Mark invalid attribute locations as being used.
+ */
+ unsigned used_locations = (max_attribute_index >= 32)
+ ? ~0 : ~((1 << max_attribute_index) - 1);
+
+ gl_shader *const sh = prog->_LinkedShaders[0];
+ assert(sh->Type == GL_VERTEX_SHADER);
+
+ /* Operate in a total of four passes.
+ *
+ * 1. Invalidate the location assignments for all vertex shader inputs.
+ *
+ * 2. Assign locations for inputs that have user-defined (via
+ * glBindVertexAttribLocation) locatoins.
+ *
+ * 3. Sort the attributes without assigned locations by number of slots
+ * required in decreasing order. Fragmentation caused by attribute
+ * locations assigned by the application may prevent large attributes
+ * from having enough contiguous space.
+ *
+ * 4. Assign locations to any inputs without assigned locations.
+ */
+
+ invalidate_variable_locations(sh, ir_var_in, VERT_ATTRIB_GENERIC0);
+
+ if (prog->Attributes != NULL) {
+ for (unsigned i = 0; i < prog->Attributes->NumParameters; i++) {
+ ir_variable *const var =
+ sh->symbols->get_variable(prog->Attributes->Parameters[i].Name);
+
+ /* Note: attributes that occupy multiple slots, such as arrays or
+ * matrices, may appear in the attrib array multiple times.
+ */
+ if ((var == NULL) || (var->location != -1))
+ continue;
+
+ /* From page 61 of the OpenGL 4.0 spec:
+ *
+ * "LinkProgram will fail if the attribute bindings assigned by
+ * BindAttribLocation do not leave not enough space to assign a
+ * location for an active matrix attribute or an active attribute
+ * array, both of which require multiple contiguous generic
+ * attributes."
+ *
+ * Previous versions of the spec contain similar language but omit the
+ * bit about attribute arrays.
+ *
+ * Page 61 of the OpenGL 4.0 spec also says:
+ *
+ * "It is possible for an application to bind more than one
+ * attribute name to the same location. This is referred to as
+ * aliasing. This will only work if only one of the aliased
+ * attributes is active in the executable program, or if no path
+ * through the shader consumes more than one attribute of a set
+ * of attributes aliased to the same location. A link error can
+ * occur if the linker determines that every path through the
+ * shader consumes multiple aliased attributes, but
+ * implementations are not required to generate an error in this
+ * case."
+ *
+ * These two paragraphs are either somewhat contradictory, or I don't
+ * fully understand one or both of them.
+ */
+ /* FINISHME: The code as currently written does not support attribute
+ * FINISHME: location aliasing (see comment above).
+ */
+ const int attr = prog->Attributes->Parameters[i].StateIndexes[0];
+ const unsigned slots = count_attribute_slots(var->type);
+
+ /* Mask representing the contiguous slots that will be used by this
+ * attribute.
+ */
+ const unsigned use_mask = (1 << slots) - 1;
+
+ /* Generate a link error if the set of bits requested for this
+ * attribute overlaps any previously allocated bits.
+ */
+ if ((~(use_mask << attr) & used_locations) != used_locations) {
+ linker_error_printf(prog,
+ "insufficient contiguous attribute locations "
+ "available for vertex shader input `%s'",
+ var->name);
+ return false;
+ }
+
+ var->location = VERT_ATTRIB_GENERIC0 + attr;
+ used_locations |= (use_mask << attr);
+ }
+ }
+
+ /* Temporary storage for the set of attributes that need locations assigned.
+ */
+ struct temp_attr {
+ unsigned slots;
+ ir_variable *var;
+
+ /* Used below in the call to qsort. */
+ static int compare(const void *a, const void *b)
+ {
+ const temp_attr *const l = (const temp_attr *) a;
+ const temp_attr *const r = (const temp_attr *) b;
+
+ /* Reversed because we want a descending order sort below. */
+ return r->slots - l->slots;
+ }
+ } to_assign[16];
+
+ unsigned num_attr = 0;
+
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_in))
+ continue;
+
+ if (var->explicit_location) {
+ const unsigned slots = count_attribute_slots(var->type);
+ const unsigned use_mask = (1 << slots) - 1;
+ const int attr = var->location - VERT_ATTRIB_GENERIC0;
+
+ if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0))
+ || (var->location < 0)) {
+ linker_error_printf(prog,
+ "invalid explicit location %d specified for "
+ "`%s'\n",
+ (var->location < 0) ? var->location : attr,
+ var->name);
+ return false;
+ } else if (var->location >= VERT_ATTRIB_GENERIC0) {
+ used_locations |= (use_mask << attr);
+ }
+ }
+
+ /* The location was explicitly assigned, nothing to do here.
+ */
+ if (var->location != -1)
+ continue;
+
+ to_assign[num_attr].slots = count_attribute_slots(var->type);
+ to_assign[num_attr].var = var;
+ num_attr++;
+ }
+
+ /* If all of the attributes were assigned locations by the application (or
+ * are built-in attributes with fixed locations), return early. This should
+ * be the common case.
+ */
+ if (num_attr == 0)
+ return true;
+
+ qsort(to_assign, num_attr, sizeof(to_assign[0]), temp_attr::compare);
+
+ /* VERT_ATTRIB_GENERIC0 is a psdueo-alias for VERT_ATTRIB_POS. It can only
+ * be explicitly assigned by via glBindAttribLocation. Mark it as reserved
+ * to prevent it from being automatically allocated below.
+ */
+ find_deref_visitor find("gl_Vertex");
+ find.run(sh->ir);
+ if (find.variable_found())
+ used_locations |= (1 << 0);
+
+ for (unsigned i = 0; i < num_attr; i++) {
+ /* Mask representing the contiguous slots that will be used by this
+ * attribute.
+ */
+ const unsigned use_mask = (1 << to_assign[i].slots) - 1;
+
+ int location = find_available_slots(used_locations, to_assign[i].slots);
+
+ if (location < 0) {
+ linker_error_printf(prog,
+ "insufficient contiguous attribute locations "
+ "available for vertex shader input `%s'",
+ to_assign[i].var->name);
+ return false;
+ }
+
+ to_assign[i].var->location = VERT_ATTRIB_GENERIC0 + location;
+ used_locations |= (use_mask << location);
+ }
+
+ return true;
+}
+
+
+/**
+ * Demote shader inputs and outputs that are not used in other stages
+ */
+void
+demote_shader_inputs_and_outputs(gl_shader *sh, enum ir_variable_mode mode)
+{
+ foreach_list(node, sh->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != int(mode)))
+ continue;
+
+ /* A shader 'in' or 'out' variable is only really an input or output if
+ * its value is used by other shader stages. This will cause the variable
+ * to have a location assigned.
+ */
+ if (var->location == -1) {
+ var->mode = ir_var_auto;
+ }
+ }
+}
+
+
+void
+assign_varying_locations(struct gl_shader_program *prog,
+ gl_shader *producer, gl_shader *consumer)
+{
+ /* FINISHME: Set dynamically when geometry shader support is added. */
+ unsigned output_index = VERT_RESULT_VAR0;
+ unsigned input_index = FRAG_ATTRIB_VAR0;
+
+ /* Operate in a total of three passes.
+ *
+ * 1. Assign locations for any matching inputs and outputs.
+ *
+ * 2. Mark output variables in the producer that do not have locations as
+ * not being outputs. This lets the optimizer eliminate them.
+ *
+ * 3. Mark input variables in the consumer that do not have locations as
+ * not being inputs. This lets the optimizer eliminate them.
+ */
+
+ invalidate_variable_locations(producer, ir_var_out, VERT_RESULT_VAR0);
+ invalidate_variable_locations(consumer, ir_var_in, FRAG_ATTRIB_VAR0);
+
+ foreach_list(node, producer->ir) {
+ ir_variable *const output_var = ((ir_instruction *) node)->as_variable();
+
+ if ((output_var == NULL) || (output_var->mode != ir_var_out)
+ || (output_var->location != -1))
+ continue;
+
+ ir_variable *const input_var =
+ consumer->symbols->get_variable(output_var->name);
+
+ if ((input_var == NULL) || (input_var->mode != ir_var_in))
+ continue;
+
+ assert(input_var->location == -1);
+
+ output_var->location = output_index;
+ input_var->location = input_index;
+
+ /* FINISHME: Support for "varying" records in GLSL 1.50. */
+ assert(!output_var->type->is_record());
+
+ if (output_var->type->is_array()) {
+ const unsigned slots = output_var->type->length
+ * output_var->type->fields.array->matrix_columns;
+
+ output_index += slots;
+ input_index += slots;
+ } else {
+ const unsigned slots = output_var->type->matrix_columns;
+
+ output_index += slots;
+ input_index += slots;
+ }
+ }
+
+ foreach_list(node, consumer->ir) {
+ ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+ if ((var == NULL) || (var->mode != ir_var_in))
+ continue;
+
+ if (var->location == -1) {
+ if (prog->Version <= 120) {
+ /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:
+ *
+ * Only those varying variables used (i.e. read) in
+ * the fragment shader executable must be written to
+ * by the vertex shader executable; declaring
+ * superfluous varying variables in a vertex shader is
+ * permissible.
+ *
+ * We interpret this text as meaning that the VS must
+ * write the variable for the FS to read it. See
+ * "glsl1-varying read but not written" in piglit.
+ */
+
+ linker_error_printf(prog, "fragment shader varying %s not written "
+ "by vertex shader\n.", var->name);
+ prog->LinkStatus = false;
+ }
+
+ /* An 'in' variable is only really a shader input if its
+ * value is written by the previous stage.
+ */
+ var->mode = ir_var_auto;
+ }
+ }
+}
+
+
+void
+link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
+{
+ void *mem_ctx = ralloc_context(NULL); // temporary linker context
+
+ prog->LinkStatus = false;
+ prog->Validated = false;
+ prog->_Used = false;
+
+ if (prog->InfoLog != NULL)
+ ralloc_free(prog->InfoLog);
+
+ prog->InfoLog = ralloc_strdup(NULL, "");
+
+ /* Separate the shaders into groups based on their type.
+ */
+ struct gl_shader **vert_shader_list;
+ unsigned num_vert_shaders = 0;
+ struct gl_shader **frag_shader_list;
+ unsigned num_frag_shaders = 0;
+
+ vert_shader_list = (struct gl_shader **)
+ calloc(2 * prog->NumShaders, sizeof(struct gl_shader *));
+ frag_shader_list = &vert_shader_list[prog->NumShaders];
+
+ unsigned min_version = UINT_MAX;
+ unsigned max_version = 0;
+ for (unsigned i = 0; i < prog->NumShaders; i++) {
+ min_version = MIN2(min_version, prog->Shaders[i]->Version);
+ max_version = MAX2(max_version, prog->Shaders[i]->Version);
+
+ switch (prog->Shaders[i]->Type) {
+ case GL_VERTEX_SHADER:
+ vert_shader_list[num_vert_shaders] = prog->Shaders[i];
+ num_vert_shaders++;
+ break;
+ case GL_FRAGMENT_SHADER:
+ frag_shader_list[num_frag_shaders] = prog->Shaders[i];
+ num_frag_shaders++;
+ break;
+ case GL_GEOMETRY_SHADER:
+ /* FINISHME: Support geometry shaders. */
+ assert(prog->Shaders[i]->Type != GL_GEOMETRY_SHADER);
+ break;
+ }
+ }
+
+ /* Previous to GLSL version 1.30, different compilation units could mix and
+ * match shading language versions. With GLSL 1.30 and later, the versions
+ * of all shaders must match.
+ */
+ assert(min_version >= 100);
+ assert(max_version <= 130);
+ if ((max_version >= 130 || min_version == 100)
+ && min_version != max_version) {
+ linker_error_printf(prog, "all shaders must use same shading "
+ "language version\n");
+ goto done;
+ }
+
+ prog->Version = max_version;
+
+ for (unsigned int i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] != NULL)
+ ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]);
+
+ prog->_LinkedShaders[i] = NULL;
+ }
+
+ /* Link all shaders for a particular stage and validate the result.
+ */
+ if (num_vert_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
+ num_vert_shaders);
+
+ if (sh == NULL)
+ goto done;
+
+ if (!validate_vertex_shader_executable(prog, sh))
+ goto done;
+
+ _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
+ sh);
+ }
+
+ if (num_frag_shaders > 0) {
+ gl_shader *const sh =
+ link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
+ num_frag_shaders);
+
+ if (sh == NULL)
+ goto done;
+
+ if (!validate_fragment_shader_executable(prog, sh))
+ goto done;
+
+ _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
+ sh);
+ }
+
+ /* Here begins the inter-stage linking phase. Some initial validation is
+ * performed, then locations are assigned for uniforms, attributes, and
+ * varyings.
+ */
+ if (cross_validate_uniforms(prog)) {
+ unsigned prev;
+
+ for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
+ if (prog->_LinkedShaders[prev] != NULL)
+ break;
+ }
+
+ /* Validate the inputs of each stage with the output of the preceeding
+ * stage.
+ */
+ for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ if (!cross_validate_outputs_to_inputs(prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]))
+ goto done;
+
+ prev = i;
+ }
+
+ prog->LinkStatus = true;
+ }
+
+ /* Do common optimization before assigning storage for attributes,
+ * uniforms, and varyings. Later optimization could possibly make
+ * some of that unused.
+ */
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32))
+ ;
+ }
+
+ update_array_sizes(prog);
+
+ assign_uniform_locations(prog);
+
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
+ /* FINISHME: The value of the max_attribute_index parameter is
+ * FINISHME: implementation dependent based on the value of
+ * FINISHME: GL_MAX_VERTEX_ATTRIBS. GL_MAX_VERTEX_ATTRIBS must be
+ * FINISHME: at least 16, so hardcode 16 for now.
+ */
+ if (!assign_attribute_locations(prog, 16)) {
+ prog->LinkStatus = false;
+ goto done;
+ }
+ }
+
+ unsigned prev;
+ for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
+ if (prog->_LinkedShaders[prev] != NULL)
+ break;
+ }
+
+ for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ assign_varying_locations(prog,
+ prog->_LinkedShaders[prev],
+ prog->_LinkedShaders[i]);
+ prev = i;
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
+ demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX],
+ ir_var_out);
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
+ gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
+
+ demote_shader_inputs_and_outputs(sh, ir_var_in);
+ demote_shader_inputs_and_outputs(sh, ir_var_inout);
+ demote_shader_inputs_and_outputs(sh, ir_var_out);
+ }
+
+ if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) {
+ gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+
+ demote_shader_inputs_and_outputs(sh, ir_var_in);
+ }
+
+ /* OpenGL ES requires that a vertex shader and a fragment shader both be
+ * present in a linked program. By checking for use of shading language
+ * version 1.00, we also catch the GL_ARB_ES2_compatibility case.
+ */
+ if (ctx->API == API_OPENGLES2 || prog->Version == 100) {
+ if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
+ linker_error_printf(prog, "program lacks a vertex shader\n");
+ prog->LinkStatus = false;
+ } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
+ linker_error_printf(prog, "program lacks a fragment shader\n");
+ prog->LinkStatus = false;
+ }
+ }
+
+ /* FINISHME: Assign fragment shader output locations. */
+
+done:
+ free(vert_shader_list);
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
+ if (prog->_LinkedShaders[i] == NULL)
+ continue;
+
+ /* Retain any live IR, but trash the rest. */
+ reparent_ir(prog->_LinkedShaders[i]->ir, prog->_LinkedShaders[i]->ir);
+ }
+
+ ralloc_free(mem_ctx);
+}
diff --git a/mesalib/src/glsl/list.h b/mesalib/src/glsl/list.h index 1d46365fa..fb6f0fbba 100644 --- a/mesalib/src/glsl/list.h +++ b/mesalib/src/glsl/list.h @@ -90,6 +90,10 @@ struct exec_node { /* If the user *does* call delete, that's OK, we will just * ralloc_free in that case. */ + static void operator delete(void *node, void *ctx) + { + ralloc_free(node); + } static void operator delete(void *node) { ralloc_free(node); @@ -299,6 +303,10 @@ struct exec_list { /* If the user *does* call delete, that's OK, we will just * ralloc_free in that case. */ + static void operator delete(void *node, void *ctx) + { + ralloc_free(node); + } static void operator delete(void *node) { ralloc_free(node); diff --git a/mesalib/src/glsl/loop_controls.cpp b/mesalib/src/glsl/loop_controls.cpp index 9acbadc50..9b77ef460 100644 --- a/mesalib/src/glsl/loop_controls.cpp +++ b/mesalib/src/glsl/loop_controls.cpp @@ -1,304 +1,304 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include <limits.h> -#include "main/compiler.h" -#include "glsl_types.h" -#include "loop_analysis.h" -#include "ir_hierarchical_visitor.h" - -/** - * Find an initializer of a variable outside a loop - * - * Works backwards from the loop to find the pre-loop value of the variable. - * This is used, for example, to find the initial value of loop induction - * variables. - * - * \param loop Loop where \c var is an induction variable - * \param var Variable whose initializer is to be found - * - * \return - * The \c ir_rvalue assigned to the variable outside the loop. May return - * \c NULL if no initializer can be found. - */ -ir_rvalue * -find_initial_value(ir_loop *loop, ir_variable *var) -{ - for (exec_node *node = loop->prev; - !node->is_head_sentinel(); - node = node->prev) { - ir_instruction *ir = (ir_instruction *) node; - - switch (ir->ir_type) { - case ir_type_call: - case ir_type_loop: - case ir_type_loop_jump: - case ir_type_return: - case ir_type_if: - return NULL; - - case ir_type_function: - case ir_type_function_signature: - assert(!"Should not get here."); - return NULL; - - case ir_type_assignment: { - ir_assignment *assign = ir->as_assignment(); - ir_variable *assignee = assign->lhs->whole_variable_referenced(); - - if (assignee == var) - return (assign->condition != NULL) ? NULL : assign->rhs; - - break; - } - - default: - break; - } - } - - return NULL; -} - - -int -calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment, - enum ir_expression_operation op) -{ - if (from == NULL || to == NULL || increment == NULL) - return -1; - - void *mem_ctx = ralloc_context(NULL); - - ir_expression *const sub = - new(mem_ctx) ir_expression(ir_binop_sub, from->type, to, from); - - ir_expression *const div = - new(mem_ctx) ir_expression(ir_binop_div, sub->type, sub, increment); - - ir_constant *iter = div->constant_expression_value(); - - if (iter == NULL) - return -1; - - if (!iter->type->is_integer()) { - ir_rvalue *cast = - new(mem_ctx) ir_expression(ir_unop_f2i, glsl_type::int_type, iter, - NULL); - - iter = cast->constant_expression_value(); - } - - int iter_value = iter->get_int_component(0); - - /* Make sure that the calculated number of iterations satisfies the exit - * condition. This is needed to catch off-by-one errors and some types of - * ill-formed loops. For example, we need to detect that the following - * loop does not have a maximum iteration count. - * - * for (float x = 0.0; x != 0.9; x += 0.2) - * ; - */ - const int bias[] = { -1, 0, 1 }; - bool valid_loop = false; - - for (unsigned i = 0; i < Elements(bias); i++) { - iter = (increment->type->is_integer()) - ? new(mem_ctx) ir_constant(iter_value + bias[i]) - : new(mem_ctx) ir_constant(float(iter_value + bias[i])); - - ir_expression *const mul = - new(mem_ctx) ir_expression(ir_binop_mul, increment->type, iter, - increment); - - ir_expression *const add = - new(mem_ctx) ir_expression(ir_binop_add, mul->type, mul, from); - - ir_expression *const cmp = - new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to); - - ir_constant *const cmp_result = cmp->constant_expression_value(); - - assert(cmp_result != NULL); - if (cmp_result->get_bool_component(0)) { - iter_value += bias[i]; - valid_loop = true; - break; - } - } - - ralloc_free(mem_ctx); - return (valid_loop) ? iter_value : -1; -} - - -class loop_control_visitor : public ir_hierarchical_visitor { -public: - loop_control_visitor(loop_state *state) - { - this->state = state; - this->progress = false; - } - - virtual ir_visitor_status visit_leave(ir_loop *ir); - - loop_state *state; - - bool progress; -}; - - -ir_visitor_status -loop_control_visitor::visit_leave(ir_loop *ir) -{ - loop_variable_state *const ls = this->state->get(ir); - - /* If we've entered a loop that hasn't been analyzed, something really, - * really bad has happened. - */ - if (ls == NULL) { - assert(ls != NULL); - return visit_continue; - } - - /* Search the loop terminating conditions for one of the form 'i < c' where - * i is a loop induction variable, c is a constant, and < is any relative - * operator. - */ - int max_iterations = ls->max_iterations; - - if(ir->from && ir->to && ir->increment) - max_iterations = calculate_iterations(ir->from, ir->to, ir->increment, (ir_expression_operation)ir->cmp); - - if(max_iterations < 0) - max_iterations = INT_MAX; - - foreach_list(node, &ls->terminators) { - loop_terminator *t = (loop_terminator *) node; - ir_if *if_stmt = t->ir; - - /* If-statements can be either 'if (expr)' or 'if (deref)'. We only care - * about the former here. - */ - ir_expression *cond = if_stmt->condition->as_expression(); - if (cond == NULL) - continue; - - switch (cond->operation) { - case ir_binop_less: - case ir_binop_greater: - case ir_binop_lequal: - case ir_binop_gequal: { - /* The expressions that we care about will either be of the form - * 'counter < limit' or 'limit < counter'. Figure out which is - * which. - */ - ir_rvalue *counter = cond->operands[0]->as_dereference_variable(); - ir_constant *limit = cond->operands[1]->as_constant(); - enum ir_expression_operation cmp = cond->operation; - - if (limit == NULL) { - counter = cond->operands[1]->as_dereference_variable(); - limit = cond->operands[0]->as_constant(); - - switch (cmp) { - case ir_binop_less: cmp = ir_binop_gequal; break; - case ir_binop_greater: cmp = ir_binop_lequal; break; - case ir_binop_lequal: cmp = ir_binop_greater; break; - case ir_binop_gequal: cmp = ir_binop_less; break; - default: assert(!"Should not get here."); - } - } - - if ((counter == NULL) || (limit == NULL)) - break; - - ir_variable *var = counter->variable_referenced(); - - ir_rvalue *init = find_initial_value(ir, var); - - foreach_list(iv_node, &ls->induction_variables) { - loop_variable *lv = (loop_variable *) iv_node; - - if (lv->var == var) { - const int iterations = calculate_iterations(init, limit, - lv->increment, - cmp); - if (iterations >= 0) { - /* If the new iteration count is lower than the previously - * believed iteration count, update the loop control values. - */ - if (iterations < max_iterations) { - ir->from = init->clone(ir, NULL); - ir->to = limit->clone(ir, NULL); - ir->increment = lv->increment->clone(ir, NULL); - ir->counter = lv->var; - ir->cmp = cmp; - - max_iterations = iterations; - } - - /* Remove the conditional break statement. The loop - * controls are now set such that the exit condition will be - * satisfied. - */ - if_stmt->remove(); - - assert(ls->num_loop_jumps > 0); - ls->num_loop_jumps--; - - this->progress = true; - } - - break; - } - } - break; - } - - default: - break; - } - } - - /* If we have proven the one of the loop exit conditions is satisifed before - * running the loop once, remove the loop. - */ - if (max_iterations == 0) - ir->remove(); - else - ls->max_iterations = max_iterations; - - return visit_continue; -} - - -bool -set_loop_controls(exec_list *instructions, loop_state *ls) -{ - loop_control_visitor v(ls); - - v.run(instructions); - - return v.progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <limits.h>
+#include "main/compiler.h"
+#include "glsl_types.h"
+#include "loop_analysis.h"
+#include "ir_hierarchical_visitor.h"
+
+/**
+ * Find an initializer of a variable outside a loop
+ *
+ * Works backwards from the loop to find the pre-loop value of the variable.
+ * This is used, for example, to find the initial value of loop induction
+ * variables.
+ *
+ * \param loop Loop where \c var is an induction variable
+ * \param var Variable whose initializer is to be found
+ *
+ * \return
+ * The \c ir_rvalue assigned to the variable outside the loop. May return
+ * \c NULL if no initializer can be found.
+ */
+ir_rvalue *
+find_initial_value(ir_loop *loop, ir_variable *var)
+{
+ for (exec_node *node = loop->prev;
+ !node->is_head_sentinel();
+ node = node->prev) {
+ ir_instruction *ir = (ir_instruction *) node;
+
+ switch (ir->ir_type) {
+ case ir_type_call:
+ case ir_type_loop:
+ case ir_type_loop_jump:
+ case ir_type_return:
+ case ir_type_if:
+ return NULL;
+
+ case ir_type_function:
+ case ir_type_function_signature:
+ assert(!"Should not get here.");
+ return NULL;
+
+ case ir_type_assignment: {
+ ir_assignment *assign = ir->as_assignment();
+ ir_variable *assignee = assign->lhs->whole_variable_referenced();
+
+ if (assignee == var)
+ return (assign->condition != NULL) ? NULL : assign->rhs;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+int
+calculate_iterations(ir_rvalue *from, ir_rvalue *to, ir_rvalue *increment,
+ enum ir_expression_operation op)
+{
+ if (from == NULL || to == NULL || increment == NULL)
+ return -1;
+
+ void *mem_ctx = ralloc_context(NULL);
+
+ ir_expression *const sub =
+ new(mem_ctx) ir_expression(ir_binop_sub, from->type, to, from);
+
+ ir_expression *const div =
+ new(mem_ctx) ir_expression(ir_binop_div, sub->type, sub, increment);
+
+ ir_constant *iter = div->constant_expression_value();
+
+ if (iter == NULL)
+ return -1;
+
+ if (!iter->type->is_integer()) {
+ ir_rvalue *cast =
+ new(mem_ctx) ir_expression(ir_unop_f2i, glsl_type::int_type, iter,
+ NULL);
+
+ iter = cast->constant_expression_value();
+ }
+
+ int iter_value = iter->get_int_component(0);
+
+ /* Make sure that the calculated number of iterations satisfies the exit
+ * condition. This is needed to catch off-by-one errors and some types of
+ * ill-formed loops. For example, we need to detect that the following
+ * loop does not have a maximum iteration count.
+ *
+ * for (float x = 0.0; x != 0.9; x += 0.2)
+ * ;
+ */
+ const int bias[] = { -1, 0, 1 };
+ bool valid_loop = false;
+
+ for (unsigned i = 0; i < Elements(bias); i++) {
+ iter = (increment->type->is_integer())
+ ? new(mem_ctx) ir_constant(iter_value + bias[i])
+ : new(mem_ctx) ir_constant(float(iter_value + bias[i]));
+
+ ir_expression *const mul =
+ new(mem_ctx) ir_expression(ir_binop_mul, increment->type, iter,
+ increment);
+
+ ir_expression *const add =
+ new(mem_ctx) ir_expression(ir_binop_add, mul->type, mul, from);
+
+ ir_expression *const cmp =
+ new(mem_ctx) ir_expression(op, glsl_type::bool_type, add, to);
+
+ ir_constant *const cmp_result = cmp->constant_expression_value();
+
+ assert(cmp_result != NULL);
+ if (cmp_result->get_bool_component(0)) {
+ iter_value += bias[i];
+ valid_loop = true;
+ break;
+ }
+ }
+
+ ralloc_free(mem_ctx);
+ return (valid_loop) ? iter_value : -1;
+}
+
+
+class loop_control_visitor : public ir_hierarchical_visitor {
+public:
+ loop_control_visitor(loop_state *state)
+ {
+ this->state = state;
+ this->progress = false;
+ }
+
+ virtual ir_visitor_status visit_leave(ir_loop *ir);
+
+ loop_state *state;
+
+ bool progress;
+};
+
+
+ir_visitor_status
+loop_control_visitor::visit_leave(ir_loop *ir)
+{
+ loop_variable_state *const ls = this->state->get(ir);
+
+ /* If we've entered a loop that hasn't been analyzed, something really,
+ * really bad has happened.
+ */
+ if (ls == NULL) {
+ assert(ls != NULL);
+ return visit_continue;
+ }
+
+ /* Search the loop terminating conditions for one of the form 'i < c' where
+ * i is a loop induction variable, c is a constant, and < is any relative
+ * operator.
+ */
+ int max_iterations = ls->max_iterations;
+
+ if(ir->from && ir->to && ir->increment)
+ max_iterations = calculate_iterations(ir->from, ir->to, ir->increment, (ir_expression_operation)ir->cmp);
+
+ if(max_iterations < 0)
+ max_iterations = INT_MAX;
+
+ foreach_list(node, &ls->terminators) {
+ loop_terminator *t = (loop_terminator *) node;
+ ir_if *if_stmt = t->ir;
+
+ /* If-statements can be either 'if (expr)' or 'if (deref)'. We only care
+ * about the former here.
+ */
+ ir_expression *cond = if_stmt->condition->as_expression();
+ if (cond == NULL)
+ continue;
+
+ switch (cond->operation) {
+ case ir_binop_less:
+ case ir_binop_greater:
+ case ir_binop_lequal:
+ case ir_binop_gequal: {
+ /* The expressions that we care about will either be of the form
+ * 'counter < limit' or 'limit < counter'. Figure out which is
+ * which.
+ */
+ ir_rvalue *counter = cond->operands[0]->as_dereference_variable();
+ ir_constant *limit = cond->operands[1]->as_constant();
+ enum ir_expression_operation cmp = cond->operation;
+
+ if (limit == NULL) {
+ counter = cond->operands[1]->as_dereference_variable();
+ limit = cond->operands[0]->as_constant();
+
+ switch (cmp) {
+ case ir_binop_less: cmp = ir_binop_gequal; break;
+ case ir_binop_greater: cmp = ir_binop_lequal; break;
+ case ir_binop_lequal: cmp = ir_binop_greater; break;
+ case ir_binop_gequal: cmp = ir_binop_less; break;
+ default: assert(!"Should not get here.");
+ }
+ }
+
+ if ((counter == NULL) || (limit == NULL))
+ break;
+
+ ir_variable *var = counter->variable_referenced();
+
+ ir_rvalue *init = find_initial_value(ir, var);
+
+ foreach_list(iv_node, &ls->induction_variables) {
+ loop_variable *lv = (loop_variable *) iv_node;
+
+ if (lv->var == var) {
+ const int iterations = calculate_iterations(init, limit,
+ lv->increment,
+ cmp);
+ if (iterations >= 0) {
+ /* If the new iteration count is lower than the previously
+ * believed iteration count, update the loop control values.
+ */
+ if (iterations < max_iterations) {
+ ir->from = init->clone(ir, NULL);
+ ir->to = limit->clone(ir, NULL);
+ ir->increment = lv->increment->clone(ir, NULL);
+ ir->counter = lv->var;
+ ir->cmp = cmp;
+
+ max_iterations = iterations;
+ }
+
+ /* Remove the conditional break statement. The loop
+ * controls are now set such that the exit condition will be
+ * satisfied.
+ */
+ if_stmt->remove();
+
+ assert(ls->num_loop_jumps > 0);
+ ls->num_loop_jumps--;
+
+ this->progress = true;
+ }
+
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ /* If we have proven the one of the loop exit conditions is satisifed before
+ * running the loop once, remove the loop.
+ */
+ if (max_iterations == 0)
+ ir->remove();
+ else
+ ls->max_iterations = max_iterations;
+
+ return visit_continue;
+}
+
+
+bool
+set_loop_controls(exec_list *instructions, loop_state *ls)
+{
+ loop_control_visitor v(ls);
+
+ v.run(instructions);
+
+ return v.progress;
+}
diff --git a/mesalib/src/glsl/lower_mat_op_to_vec.cpp b/mesalib/src/glsl/lower_mat_op_to_vec.cpp index 8cbbfa713..dc46ed417 100644 --- a/mesalib/src/glsl/lower_mat_op_to_vec.cpp +++ b/mesalib/src/glsl/lower_mat_op_to_vec.cpp @@ -1,490 +1,490 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file lower_mat_op_to_vec.cpp - * - * Breaks matrix operation expressions down to a series of vector operations. - * - * Generally this is how we have to codegen matrix operations for a - * GPU, so this gives us the chance to constant fold operations on a - * column or row. - */ - -#include "ir.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" - -class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor { -public: - ir_mat_op_to_vec_visitor() - { - this->made_progress = false; - this->mem_ctx = NULL; - } - - ir_visitor_status visit_leave(ir_assignment *); - - ir_dereference *get_column(ir_variable *var, int col); - ir_rvalue *get_element(ir_variable *var, int col, int row); - - void do_mul_mat_mat(ir_variable *result_var, - ir_variable *a_var, ir_variable *b_var); - void do_mul_mat_vec(ir_variable *result_var, - ir_variable *a_var, ir_variable *b_var); - void do_mul_vec_mat(ir_variable *result_var, - ir_variable *a_var, ir_variable *b_var); - void do_mul_mat_scalar(ir_variable *result_var, - ir_variable *a_var, ir_variable *b_var); - void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var, - ir_variable *b_var, bool test_equal); - - void *mem_ctx; - bool made_progress; -}; - -static bool -mat_op_to_vec_predicate(ir_instruction *ir) -{ - ir_expression *expr = ir->as_expression(); - unsigned int i; - - if (!expr) - return false; - - for (i = 0; i < expr->get_num_operands(); i++) { - if (expr->operands[i]->type->is_matrix()) - return true; - } - - return false; -} - -bool -do_mat_op_to_vec(exec_list *instructions) -{ - ir_mat_op_to_vec_visitor v; - - /* Pull out any matrix expression to a separate assignment to a - * temp. This will make our handling of the breakdown to - * operations on the matrix's vector components much easier. - */ - do_expression_flattening(instructions, mat_op_to_vec_predicate); - - visit_list_elements(&v, instructions); - - return v.made_progress; -} - -ir_rvalue * -ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row) -{ - ir_dereference *deref; - - deref = new(mem_ctx) ir_dereference_variable(var); - - if (var->type->is_matrix()) { - deref = new(mem_ctx) ir_dereference_array(var, - new(mem_ctx) ir_constant(col)); - } else { - assert(col == 0); - } - - return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1); -} - -ir_dereference * -ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row) -{ - ir_dereference *deref; - - if (!var->type->is_matrix()) { - deref = new(mem_ctx) ir_dereference_variable(var); - } else { - deref = new(mem_ctx) ir_dereference_variable(var); - deref = new(mem_ctx) ir_dereference_array(deref, - new(mem_ctx) ir_constant(row)); - } - - return deref; -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var, - ir_variable *a_var, - ir_variable *b_var) -{ - int b_col, i; - ir_assignment *assign; - ir_expression *expr; - - for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) { - ir_rvalue *a = get_column(a_var, 0); - ir_rvalue *b = get_element(b_var, b_col, 0); - - /* first column */ - expr = new(mem_ctx) ir_expression(ir_binop_mul, - a->type, - a, - b); - - /* following columns */ - for (i = 1; i < a_var->type->matrix_columns; i++) { - ir_expression *mul_expr; - - a = get_column(a_var, i); - b = get_element(b_var, b_col, i); - - mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, - a->type, - a, - b); - expr = new(mem_ctx) ir_expression(ir_binop_add, - a->type, - expr, - mul_expr); - } - - ir_rvalue *result = get_column(result_var, b_col); - assign = new(mem_ctx) ir_assignment(result, - expr, - NULL); - base_ir->insert_before(assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var, - ir_variable *a_var, - ir_variable *b_var) -{ - int i; - ir_rvalue *a = get_column(a_var, 0); - ir_rvalue *b = get_element(b_var, 0, 0); - ir_assignment *assign; - ir_expression *expr; - - /* first column */ - expr = new(mem_ctx) ir_expression(ir_binop_mul, - result_var->type, - a, - b); - - /* following columns */ - for (i = 1; i < a_var->type->matrix_columns; i++) { - ir_expression *mul_expr; - - a = get_column(a_var, i); - b = get_element(b_var, 0, i); - - mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, - result_var->type, - a, - b); - expr = new(mem_ctx) ir_expression(ir_binop_add, - result_var->type, - expr, - mul_expr); - } - - ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var); - assign = new(mem_ctx) ir_assignment(result, - expr, - NULL); - base_ir->insert_before(assign); -} - -void -ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var, - ir_variable *a_var, - ir_variable *b_var) -{ - int i; - - for (i = 0; i < b_var->type->matrix_columns; i++) { - ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var); - ir_rvalue *b = get_column(b_var, i); - ir_rvalue *result; - ir_expression *column_expr; - ir_assignment *column_assign; - - result = new(mem_ctx) ir_dereference_variable(result_var); - result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1); - - column_expr = new(mem_ctx) ir_expression(ir_binop_dot, - result->type, - a, - b); - - column_assign = new(mem_ctx) ir_assignment(result, - column_expr, - NULL); - base_ir->insert_before(column_assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var, - ir_variable *a_var, - ir_variable *b_var) -{ - int i; - - for (i = 0; i < a_var->type->matrix_columns; i++) { - ir_rvalue *a = get_column(a_var, i); - ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var); - ir_rvalue *result = get_column(result_var, i); - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(ir_binop_mul, - result->type, - a, - b); - - column_assign = new(mem_ctx) ir_assignment(result, - column_expr, - NULL); - base_ir->insert_before(column_assign); - } -} - -void -ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var, - ir_variable *a_var, - ir_variable *b_var, - bool test_equal) -{ - /* This essentially implements the following GLSL: - * - * bool equal(mat4 a, mat4 b) - * { - * return !any(bvec4(a[0] != b[0], - * a[1] != b[1], - * a[2] != b[2], - * a[3] != b[3]); - * } - * - * bool nequal(mat4 a, mat4 b) - * { - * return any(bvec4(a[0] != b[0], - * a[1] != b[1], - * a[2] != b[2], - * a[3] != b[3]); - * } - */ - const unsigned columns = a_var->type->matrix_columns; - const glsl_type *const bvec_type = - glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1); - - ir_variable *const tmp_bvec = - new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec", - ir_var_temporary); - this->base_ir->insert_before(tmp_bvec); - - for (unsigned i = 0; i < columns; i++) { - ir_dereference *const op0 = get_column(a_var, i); - ir_dereference *const op1 = get_column(b_var, i); - - ir_expression *const cmp = - new(this->mem_ctx) ir_expression(ir_binop_any_nequal, - glsl_type::bool_type, op0, op1); - - ir_dereference *const lhs = - new(this->mem_ctx) ir_dereference_variable(tmp_bvec); - - ir_assignment *const assign = - new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i)); - - this->base_ir->insert_before(assign); - } - - ir_rvalue *const val = - new(this->mem_ctx) ir_dereference_variable(tmp_bvec); - - ir_expression *any = - new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type, - val, NULL); - - if (test_equal) - any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, - glsl_type::bool_type, - any, NULL); - - ir_rvalue *const result = - new(this->mem_ctx) ir_dereference_variable(result_var); - - ir_assignment *const assign = - new(mem_ctx) ir_assignment(result, any, NULL); - base_ir->insert_before(assign); -} - -static bool -has_matrix_operand(const ir_expression *expr, unsigned &columns) -{ - for (unsigned i = 0; i < expr->get_num_operands(); i++) { - if (expr->operands[i]->type->is_matrix()) { - columns = expr->operands[i]->type->matrix_columns; - return true; - } - } - - return false; -} - - -ir_visitor_status -ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign) -{ - ir_expression *orig_expr = orig_assign->rhs->as_expression(); - unsigned int i, matrix_columns = 1; - ir_variable *op_var[2]; - - if (!orig_expr) - return visit_continue; - - if (!has_matrix_operand(orig_expr, matrix_columns)) - return visit_continue; - - assert(orig_expr->get_num_operands() <= 2); - - mem_ctx = ralloc_parent(orig_assign); - - ir_dereference_variable *lhs_deref = - orig_assign->lhs->as_dereference_variable(); - assert(lhs_deref); - - ir_variable *result_var = lhs_deref->var; - - /* Store the expression operands in temps so we can use them - * multiple times. - */ - for (i = 0; i < orig_expr->get_num_operands(); i++) { - ir_assignment *assign; - - op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type, - "mat_op_to_vec", - ir_var_temporary); - base_ir->insert_before(op_var[i]); - - lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]); - assign = new(mem_ctx) ir_assignment(lhs_deref, - orig_expr->operands[i], - NULL); - base_ir->insert_before(assign); - } - - /* OK, time to break down this matrix operation. */ - switch (orig_expr->operation) { - case ir_unop_neg: { - const unsigned mask = (1U << result_var->type->vector_elements) - 1; - - /* Apply the operation to each column.*/ - for (i = 0; i < matrix_columns; i++) { - ir_rvalue *op0 = get_column(op_var[0], i); - ir_dereference *result = get_column(result_var, i); - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(orig_expr->operation, - result->type, - op0, - NULL); - - column_assign = new(mem_ctx) ir_assignment(result, - column_expr, - NULL, - mask); - assert(column_assign->write_mask != 0); - base_ir->insert_before(column_assign); - } - break; - } - case ir_binop_add: - case ir_binop_sub: - case ir_binop_div: - case ir_binop_mod: { - const unsigned mask = (1U << result_var->type->vector_elements) - 1; - - /* For most operations, the matrix version is just going - * column-wise through and applying the operation to each column - * if available. - */ - for (i = 0; i < matrix_columns; i++) { - ir_rvalue *op0 = get_column(op_var[0], i); - ir_rvalue *op1 = get_column(op_var[1], i); - ir_dereference *result = get_column(result_var, i); - ir_expression *column_expr; - ir_assignment *column_assign; - - column_expr = new(mem_ctx) ir_expression(orig_expr->operation, - result->type, - op0, - op1); - - column_assign = new(mem_ctx) ir_assignment(result, - column_expr, - NULL, - mask); - assert(column_assign->write_mask != 0); - base_ir->insert_before(column_assign); - } - break; - } - case ir_binop_mul: - if (op_var[0]->type->is_matrix()) { - if (op_var[1]->type->is_matrix()) { - do_mul_mat_mat(result_var, op_var[0], op_var[1]); - } else if (op_var[1]->type->is_vector()) { - do_mul_mat_vec(result_var, op_var[0], op_var[1]); - } else { - assert(op_var[1]->type->is_scalar()); - do_mul_mat_scalar(result_var, op_var[0], op_var[1]); - } - } else { - assert(op_var[1]->type->is_matrix()); - if (op_var[0]->type->is_vector()) { - do_mul_vec_mat(result_var, op_var[0], op_var[1]); - } else { - assert(op_var[0]->type->is_scalar()); - do_mul_mat_scalar(result_var, op_var[1], op_var[0]); - } - } - break; - - case ir_binop_all_equal: - case ir_binop_any_nequal: - do_equal_mat_mat(result_var, op_var[1], op_var[0], - (orig_expr->operation == ir_binop_all_equal)); - break; - - default: - printf("FINISHME: Handle matrix operation for %s\n", - orig_expr->operator_string()); - abort(); - } - orig_assign->remove(); - this->made_progress = true; - - return visit_continue; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file lower_mat_op_to_vec.cpp
+ *
+ * Breaks matrix operation expressions down to a series of vector operations.
+ *
+ * Generally this is how we have to codegen matrix operations for a
+ * GPU, so this gives us the chance to constant fold operations on a
+ * column or row.
+ */
+
+#include "ir.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+
+class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor {
+public:
+ ir_mat_op_to_vec_visitor()
+ {
+ this->made_progress = false;
+ this->mem_ctx = NULL;
+ }
+
+ ir_visitor_status visit_leave(ir_assignment *);
+
+ ir_dereference *get_column(ir_variable *var, int col);
+ ir_rvalue *get_element(ir_variable *var, int col, int row);
+
+ void do_mul_mat_mat(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_mat_vec(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_vec_mat(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_mul_mat_scalar(ir_variable *result_var,
+ ir_variable *a_var, ir_variable *b_var);
+ void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var,
+ ir_variable *b_var, bool test_equal);
+
+ void *mem_ctx;
+ bool made_progress;
+};
+
+static bool
+mat_op_to_vec_predicate(ir_instruction *ir)
+{
+ ir_expression *expr = ir->as_expression();
+ unsigned int i;
+
+ if (!expr)
+ return false;
+
+ for (i = 0; i < expr->get_num_operands(); i++) {
+ if (expr->operands[i]->type->is_matrix())
+ return true;
+ }
+
+ return false;
+}
+
+bool
+do_mat_op_to_vec(exec_list *instructions)
+{
+ ir_mat_op_to_vec_visitor v;
+
+ /* Pull out any matrix expression to a separate assignment to a
+ * temp. This will make our handling of the breakdown to
+ * operations on the matrix's vector components much easier.
+ */
+ do_expression_flattening(instructions, mat_op_to_vec_predicate);
+
+ visit_list_elements(&v, instructions);
+
+ return v.made_progress;
+}
+
+ir_rvalue *
+ir_mat_op_to_vec_visitor::get_element(ir_variable *var, int col, int row)
+{
+ ir_dereference *deref;
+
+ deref = new(mem_ctx) ir_dereference_variable(var);
+
+ if (var->type->is_matrix()) {
+ deref = new(mem_ctx) ir_dereference_array(var,
+ new(mem_ctx) ir_constant(col));
+ } else {
+ assert(col == 0);
+ }
+
+ return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1);
+}
+
+ir_dereference *
+ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row)
+{
+ ir_dereference *deref;
+
+ if (!var->type->is_matrix()) {
+ deref = new(mem_ctx) ir_dereference_variable(var);
+ } else {
+ deref = new(mem_ctx) ir_dereference_variable(var);
+ deref = new(mem_ctx) ir_dereference_array(deref,
+ new(mem_ctx) ir_constant(row));
+ }
+
+ return deref;
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int b_col, i;
+ ir_assignment *assign;
+ ir_expression *expr;
+
+ for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) {
+ ir_rvalue *a = get_column(a_var, 0);
+ ir_rvalue *b = get_element(b_var, b_col, 0);
+
+ /* first column */
+ expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ a->type,
+ a,
+ b);
+
+ /* following columns */
+ for (i = 1; i < a_var->type->matrix_columns; i++) {
+ ir_expression *mul_expr;
+
+ a = get_column(a_var, i);
+ b = get_element(b_var, b_col, i);
+
+ mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ a->type,
+ a,
+ b);
+ expr = new(mem_ctx) ir_expression(ir_binop_add,
+ a->type,
+ expr,
+ mul_expr);
+ }
+
+ ir_rvalue *result = get_column(result_var, b_col);
+ assign = new(mem_ctx) ir_assignment(result,
+ expr,
+ NULL);
+ base_ir->insert_before(assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+ ir_rvalue *a = get_column(a_var, 0);
+ ir_rvalue *b = get_element(b_var, 0, 0);
+ ir_assignment *assign;
+ ir_expression *expr;
+
+ /* first column */
+ expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result_var->type,
+ a,
+ b);
+
+ /* following columns */
+ for (i = 1; i < a_var->type->matrix_columns; i++) {
+ ir_expression *mul_expr;
+
+ a = get_column(a_var, i);
+ b = get_element(b_var, 0, i);
+
+ mul_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result_var->type,
+ a,
+ b);
+ expr = new(mem_ctx) ir_expression(ir_binop_add,
+ result_var->type,
+ expr,
+ mul_expr);
+ }
+
+ ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var);
+ assign = new(mem_ctx) ir_assignment(result,
+ expr,
+ NULL);
+ base_ir->insert_before(assign);
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+
+ for (i = 0; i < b_var->type->matrix_columns; i++) {
+ ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var);
+ ir_rvalue *b = get_column(b_var, i);
+ ir_rvalue *result;
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ result = new(mem_ctx) ir_dereference_variable(result_var);
+ result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1);
+
+ column_expr = new(mem_ctx) ir_expression(ir_binop_dot,
+ result->type,
+ a,
+ b);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL);
+ base_ir->insert_before(column_assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var)
+{
+ int i;
+
+ for (i = 0; i < a_var->type->matrix_columns; i++) {
+ ir_rvalue *a = get_column(a_var, i);
+ ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var);
+ ir_rvalue *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(ir_binop_mul,
+ result->type,
+ a,
+ b);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL);
+ base_ir->insert_before(column_assign);
+ }
+}
+
+void
+ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var,
+ ir_variable *a_var,
+ ir_variable *b_var,
+ bool test_equal)
+{
+ /* This essentially implements the following GLSL:
+ *
+ * bool equal(mat4 a, mat4 b)
+ * {
+ * return !any(bvec4(a[0] != b[0],
+ * a[1] != b[1],
+ * a[2] != b[2],
+ * a[3] != b[3]);
+ * }
+ *
+ * bool nequal(mat4 a, mat4 b)
+ * {
+ * return any(bvec4(a[0] != b[0],
+ * a[1] != b[1],
+ * a[2] != b[2],
+ * a[3] != b[3]);
+ * }
+ */
+ const unsigned columns = a_var->type->matrix_columns;
+ const glsl_type *const bvec_type =
+ glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1);
+
+ ir_variable *const tmp_bvec =
+ new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec",
+ ir_var_temporary);
+ this->base_ir->insert_before(tmp_bvec);
+
+ for (unsigned i = 0; i < columns; i++) {
+ ir_dereference *const op0 = get_column(a_var, i);
+ ir_dereference *const op1 = get_column(b_var, i);
+
+ ir_expression *const cmp =
+ new(this->mem_ctx) ir_expression(ir_binop_any_nequal,
+ glsl_type::bool_type, op0, op1);
+
+ ir_dereference *const lhs =
+ new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+ ir_assignment *const assign =
+ new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i));
+
+ this->base_ir->insert_before(assign);
+ }
+
+ ir_rvalue *const val =
+ new(this->mem_ctx) ir_dereference_variable(tmp_bvec);
+
+ ir_expression *any =
+ new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type,
+ val, NULL);
+
+ if (test_equal)
+ any = new(this->mem_ctx) ir_expression(ir_unop_logic_not,
+ glsl_type::bool_type,
+ any, NULL);
+
+ ir_rvalue *const result =
+ new(this->mem_ctx) ir_dereference_variable(result_var);
+
+ ir_assignment *const assign =
+ new(mem_ctx) ir_assignment(result, any, NULL);
+ base_ir->insert_before(assign);
+}
+
+static bool
+has_matrix_operand(const ir_expression *expr, unsigned &columns)
+{
+ for (unsigned i = 0; i < expr->get_num_operands(); i++) {
+ if (expr->operands[i]->type->is_matrix()) {
+ columns = expr->operands[i]->type->matrix_columns;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+ir_visitor_status
+ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign)
+{
+ ir_expression *orig_expr = orig_assign->rhs->as_expression();
+ unsigned int i, matrix_columns = 1;
+ ir_variable *op_var[2];
+
+ if (!orig_expr)
+ return visit_continue;
+
+ if (!has_matrix_operand(orig_expr, matrix_columns))
+ return visit_continue;
+
+ assert(orig_expr->get_num_operands() <= 2);
+
+ mem_ctx = ralloc_parent(orig_assign);
+
+ ir_dereference_variable *lhs_deref =
+ orig_assign->lhs->as_dereference_variable();
+ assert(lhs_deref);
+
+ ir_variable *result_var = lhs_deref->var;
+
+ /* Store the expression operands in temps so we can use them
+ * multiple times.
+ */
+ for (i = 0; i < orig_expr->get_num_operands(); i++) {
+ ir_assignment *assign;
+
+ op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type,
+ "mat_op_to_vec",
+ ir_var_temporary);
+ base_ir->insert_before(op_var[i]);
+
+ lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]);
+ assign = new(mem_ctx) ir_assignment(lhs_deref,
+ orig_expr->operands[i],
+ NULL);
+ base_ir->insert_before(assign);
+ }
+
+ /* OK, time to break down this matrix operation. */
+ switch (orig_expr->operation) {
+ case ir_unop_neg: {
+ const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
+ /* Apply the operation to each column.*/
+ for (i = 0; i < matrix_columns; i++) {
+ ir_rvalue *op0 = get_column(op_var[0], i);
+ ir_dereference *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
+ result->type,
+ op0,
+ NULL);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL,
+ mask);
+ assert(column_assign->write_mask != 0);
+ base_ir->insert_before(column_assign);
+ }
+ break;
+ }
+ case ir_binop_add:
+ case ir_binop_sub:
+ case ir_binop_div:
+ case ir_binop_mod: {
+ const unsigned mask = (1U << result_var->type->vector_elements) - 1;
+
+ /* For most operations, the matrix version is just going
+ * column-wise through and applying the operation to each column
+ * if available.
+ */
+ for (i = 0; i < matrix_columns; i++) {
+ ir_rvalue *op0 = get_column(op_var[0], i);
+ ir_rvalue *op1 = get_column(op_var[1], i);
+ ir_dereference *result = get_column(result_var, i);
+ ir_expression *column_expr;
+ ir_assignment *column_assign;
+
+ column_expr = new(mem_ctx) ir_expression(orig_expr->operation,
+ result->type,
+ op0,
+ op1);
+
+ column_assign = new(mem_ctx) ir_assignment(result,
+ column_expr,
+ NULL,
+ mask);
+ assert(column_assign->write_mask != 0);
+ base_ir->insert_before(column_assign);
+ }
+ break;
+ }
+ case ir_binop_mul:
+ if (op_var[0]->type->is_matrix()) {
+ if (op_var[1]->type->is_matrix()) {
+ do_mul_mat_mat(result_var, op_var[0], op_var[1]);
+ } else if (op_var[1]->type->is_vector()) {
+ do_mul_mat_vec(result_var, op_var[0], op_var[1]);
+ } else {
+ assert(op_var[1]->type->is_scalar());
+ do_mul_mat_scalar(result_var, op_var[0], op_var[1]);
+ }
+ } else {
+ assert(op_var[1]->type->is_matrix());
+ if (op_var[0]->type->is_vector()) {
+ do_mul_vec_mat(result_var, op_var[0], op_var[1]);
+ } else {
+ assert(op_var[0]->type->is_scalar());
+ do_mul_mat_scalar(result_var, op_var[1], op_var[0]);
+ }
+ }
+ break;
+
+ case ir_binop_all_equal:
+ case ir_binop_any_nequal:
+ do_equal_mat_mat(result_var, op_var[1], op_var[0],
+ (orig_expr->operation == ir_binop_all_equal));
+ break;
+
+ default:
+ printf("FINISHME: Handle matrix operation for %s\n",
+ orig_expr->operator_string());
+ abort();
+ }
+ orig_assign->remove();
+ this->made_progress = true;
+
+ return visit_continue;
+}
diff --git a/mesalib/src/glsl/main.cpp b/mesalib/src/glsl/main.cpp index 096da93dc..873a4fa16 100644 --- a/mesalib/src/glsl/main.cpp +++ b/mesalib/src/glsl/main.cpp @@ -1,334 +1,345 @@ -/* - * Copyright © 2008, 2009 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#include <getopt.h> - -#include "ast.h" -#include "glsl_parser_extras.h" -#include "glsl_parser.h" -#include "ir_optimization.h" -#include "ir_print_visitor.h" -#include "program.h" -#include "loop_analysis.h" - -extern "C" struct gl_shader * -_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type); - -extern "C" void -_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr, - struct gl_shader *sh); - -/* Copied from shader_api.c for the stand-alone compiler. - */ -void -_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr, - struct gl_shader *sh) -{ - *ptr = sh; -} - -struct gl_shader * -_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type) -{ - struct gl_shader *shader; - - (void) ctx; - - assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER); - shader = rzalloc(NULL, struct gl_shader); - if (shader) { - shader->Type = type; - shader->Name = name; - shader->RefCount = 1; - } - return shader; -} - -static void -initialize_context(struct gl_context *ctx, gl_api api) -{ - memset(ctx, 0, sizeof(*ctx)); - - ctx->API = api; - - ctx->Extensions.ARB_ES2_compatibility = GL_TRUE; - ctx->Extensions.ARB_draw_buffers = GL_TRUE; - ctx->Extensions.ARB_draw_instanced = GL_TRUE; - ctx->Extensions.ARB_fragment_coord_conventions = GL_TRUE; - ctx->Extensions.EXT_texture_array = GL_TRUE; - ctx->Extensions.NV_texture_rectangle = GL_TRUE; - - /* GLSL 1.30 isn't fully supported, but we need to advertise 1.30 so that - * the built-in functions for 1.30 can be built. - */ - ctx->Const.GLSLVersion = 130; - - /* 1.10 minimums. */ - ctx->Const.MaxLights = 8; - ctx->Const.MaxClipPlanes = 8; - ctx->Const.MaxTextureUnits = 2; - - /* More than the 1.10 minimum to appease parser tests taken from - * apps that (hopefully) already checked the number of coords. - */ - ctx->Const.MaxTextureCoordUnits = 4; - - ctx->Const.VertexProgram.MaxAttribs = 16; - ctx->Const.VertexProgram.MaxUniformComponents = 512; - ctx->Const.MaxVarying = 8; - ctx->Const.MaxVertexTextureImageUnits = 0; - ctx->Const.MaxCombinedTextureImageUnits = 2; - ctx->Const.MaxTextureImageUnits = 2; - ctx->Const.FragmentProgram.MaxUniformComponents = 64; - - ctx->Const.MaxDrawBuffers = 2; - - ctx->Driver.NewShader = _mesa_new_shader; -} - -/* Returned string will have 'ctx' as its ralloc owner. */ -static char * -load_text_file(void *ctx, const char *file_name) -{ - char *text = NULL; - size_t size; - size_t total_read = 0; - FILE *fp = fopen(file_name, "rb"); - - if (!fp) { - return NULL; - } - - fseek(fp, 0L, SEEK_END); - size = ftell(fp); - fseek(fp, 0L, SEEK_SET); - - text = (char *) ralloc_size(ctx, size + 1); - if (text != NULL) { - do { - size_t bytes = fread(text + total_read, - 1, size - total_read, fp); - if (bytes < size - total_read) { - free(text); - text = NULL; - break; - } - - if (bytes == 0) { - break; - } - - total_read += bytes; - } while (total_read < size); - - text[total_read] = '\0'; - } - - fclose(fp); - - return text; -} - -int glsl_es = 0; -int dump_ast = 0; -int dump_hir = 0; -int dump_lir = 0; -int do_link = 0; - -const struct option compiler_opts[] = { - { "glsl-es", 0, &glsl_es, 1 }, - { "dump-ast", 0, &dump_ast, 1 }, - { "dump-hir", 0, &dump_hir, 1 }, - { "dump-lir", 0, &dump_lir, 1 }, - { "link", 0, &do_link, 1 }, - { NULL, 0, NULL, 0 } -}; - -/** - * \brief Print proper usage and exit with failure. - */ -void -usage_fail(const char *name) -{ - - const char *header = - "usage: %s [options] <file.vert | file.geom | file.frag>\n" - "\n" - "Possible options are:\n"; - printf(header, name, name); - for (const struct option *o = compiler_opts; o->name != 0; ++o) { - printf(" --%s\n", o->name); - } - exit(EXIT_FAILURE); -} - - -void -compile_shader(struct gl_context *ctx, struct gl_shader *shader) -{ - struct _mesa_glsl_parse_state *state = - new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); - - const char *source = shader->Source; - state->error = preprocess(state, &source, &state->info_log, - state->extensions, ctx->API) != 0; - - if (!state->error) { - _mesa_glsl_lexer_ctor(state, source); - _mesa_glsl_parse(state); - _mesa_glsl_lexer_dtor(state); - } - - if (dump_ast) { - foreach_list_const(n, &state->translation_unit) { - ast_node *ast = exec_node_data(ast_node, n, link); - ast->print(); - } - printf("\n\n"); - } - - shader->ir = new(shader) exec_list; - if (!state->error && !state->translation_unit.is_empty()) - _mesa_ast_to_hir(shader->ir, state); - - /* Print out the unoptimized IR. */ - if (!state->error && dump_hir) { - validate_ir_tree(shader->ir); - _mesa_print_ir(shader->ir, state); - } - - /* Optimization passes */ - if (!state->error && !shader->ir->is_empty()) { - bool progress; - do { - progress = do_common_optimization(shader->ir, false, 32); - } while (progress); - - validate_ir_tree(shader->ir); - } - - - /* Print out the resulting IR */ - if (!state->error && dump_lir) { - _mesa_print_ir(shader->ir, state); - } - - shader->symbols = state->symbols; - shader->CompileStatus = !state->error; - shader->Version = state->language_version; - memcpy(shader->builtins_to_link, state->builtins_to_link, - sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link); - shader->num_builtins_to_link = state->num_builtins_to_link; - - if (shader->InfoLog) - ralloc_free(shader->InfoLog); - - shader->InfoLog = state->info_log; - - /* Retain any live IR, but trash the rest. */ - reparent_ir(shader->ir, shader); - - ralloc_free(state); - - return; -} - -int -main(int argc, char **argv) -{ - int status = EXIT_SUCCESS; - struct gl_context local_ctx; - struct gl_context *ctx = &local_ctx; - - int c; - int idx = 0; - while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1) - /* empty */ ; - - - if (argc <= optind) - usage_fail(argv[0]); - - initialize_context(ctx, (glsl_es) ? API_OPENGLES2 : API_OPENGL); - - struct gl_shader_program *whole_program; - - whole_program = rzalloc (NULL, struct gl_shader_program); - assert(whole_program != NULL); - - for (/* empty */; argc > optind; optind++) { - whole_program->Shaders = - reralloc(whole_program, whole_program->Shaders, - struct gl_shader *, whole_program->NumShaders + 1); - assert(whole_program->Shaders != NULL); - - struct gl_shader *shader = rzalloc(whole_program, gl_shader); - - whole_program->Shaders[whole_program->NumShaders] = shader; - whole_program->NumShaders++; - - const unsigned len = strlen(argv[optind]); - if (len < 6) - usage_fail(argv[0]); - - const char *const ext = & argv[optind][len - 5]; - if (strncmp(".vert", ext, 5) == 0) - shader->Type = GL_VERTEX_SHADER; - else if (strncmp(".geom", ext, 5) == 0) - shader->Type = GL_GEOMETRY_SHADER; - else if (strncmp(".frag", ext, 5) == 0) - shader->Type = GL_FRAGMENT_SHADER; - else - usage_fail(argv[0]); - - shader->Source = load_text_file(whole_program, argv[optind]); - if (shader->Source == NULL) { - printf("File \"%s\" does not exist.\n", argv[optind]); - exit(EXIT_FAILURE); - } - - compile_shader(ctx, shader); - - if (!shader->CompileStatus) { - printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog); - status = EXIT_FAILURE; - break; - } - } - - if ((status == EXIT_SUCCESS) && do_link) { - link_shaders(ctx, whole_program); - status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE; - - if (strlen(whole_program->InfoLog) > 0) - printf("Info log for linking:\n%s\n", whole_program->InfoLog); - } - - for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) - ralloc_free(whole_program->_LinkedShaders[i]); - - ralloc_free(whole_program); - _mesa_glsl_release_types(); - _mesa_glsl_release_functions(); - - return status; -} +/*
+ * Copyright © 2008, 2009 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include <io.h>
+
+#ifdef _MSC_VER
+#define __STDC__ 1
+#include <getopt.h>
+#define open _open
+#define read _read
+#define fstat _fstat
+#define stat _stat
+#define close _close
+#define O_RDONLY _O_RDONLY
+#endif
+
+#include "ast.h"
+#include "glsl_parser_extras.h"
+#include "glsl_parser.h"
+#include "ir_optimization.h"
+#include "ir_print_visitor.h"
+#include "program.h"
+#include "loop_analysis.h"
+
+extern "C" struct gl_shader *
+_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type);
+
+extern "C" void
+_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
+ struct gl_shader *sh);
+
+/* Copied from shader_api.c for the stand-alone compiler.
+ */
+void
+_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
+ struct gl_shader *sh)
+{
+ *ptr = sh;
+}
+
+struct gl_shader *
+_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type)
+{
+ struct gl_shader *shader;
+
+ (void) ctx;
+
+ assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER);
+ shader = rzalloc(NULL, struct gl_shader);
+ if (shader) {
+ shader->Type = type;
+ shader->Name = name;
+ shader->RefCount = 1;
+ }
+ return shader;
+}
+
+static void
+initialize_context(struct gl_context *ctx, gl_api api)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->API = api;
+
+ ctx->Extensions.ARB_ES2_compatibility = GL_TRUE;
+ ctx->Extensions.ARB_draw_buffers = GL_TRUE;
+ ctx->Extensions.ARB_draw_instanced = GL_TRUE;
+ ctx->Extensions.ARB_fragment_coord_conventions = GL_TRUE;
+ ctx->Extensions.EXT_texture_array = GL_TRUE;
+ ctx->Extensions.NV_texture_rectangle = GL_TRUE;
+
+ /* GLSL 1.30 isn't fully supported, but we need to advertise 1.30 so that
+ * the built-in functions for 1.30 can be built.
+ */
+ ctx->Const.GLSLVersion = 130;
+
+ /* 1.10 minimums. */
+ ctx->Const.MaxLights = 8;
+ ctx->Const.MaxClipPlanes = 8;
+ ctx->Const.MaxTextureUnits = 2;
+
+ /* More than the 1.10 minimum to appease parser tests taken from
+ * apps that (hopefully) already checked the number of coords.
+ */
+ ctx->Const.MaxTextureCoordUnits = 4;
+
+ ctx->Const.VertexProgram.MaxAttribs = 16;
+ ctx->Const.VertexProgram.MaxUniformComponents = 512;
+ ctx->Const.MaxVarying = 8;
+ ctx->Const.MaxVertexTextureImageUnits = 0;
+ ctx->Const.MaxCombinedTextureImageUnits = 2;
+ ctx->Const.MaxTextureImageUnits = 2;
+ ctx->Const.FragmentProgram.MaxUniformComponents = 64;
+
+ ctx->Const.MaxDrawBuffers = 2;
+
+ ctx->Driver.NewShader = _mesa_new_shader;
+}
+
+/* Returned string will have 'ctx' as its ralloc owner. */
+static char *
+load_text_file(void *ctx, const char *file_name)
+{
+ char *text = NULL;
+ size_t size;
+ size_t total_read = 0;
+ FILE *fp = fopen(file_name, "rb");
+
+ if (!fp) {
+ return NULL;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ size = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+
+ text = (char *) ralloc_size(ctx, size + 1);
+ if (text != NULL) {
+ do {
+ size_t bytes = fread(text + total_read,
+ 1, size - total_read, fp);
+ if (bytes < size - total_read) {
+ free(text);
+ text = NULL;
+ break;
+ }
+
+ if (bytes == 0) {
+ break;
+ }
+
+ total_read += bytes;
+ } while (total_read < size);
+
+ text[total_read] = '\0';
+ }
+
+ fclose(fp);
+
+ return text;
+}
+
+int glsl_es = 0;
+int dump_ast = 0;
+int dump_hir = 0;
+int dump_lir = 0;
+int do_link = 0;
+
+const struct option compiler_opts[] = {
+ { "glsl-es", 0, &glsl_es, 1 },
+ { "dump-ast", 0, &dump_ast, 1 },
+ { "dump-hir", 0, &dump_hir, 1 },
+ { "dump-lir", 0, &dump_lir, 1 },
+ { "link", 0, &do_link, 1 },
+ { NULL, 0, NULL, 0 }
+};
+
+/**
+ * \brief Print proper usage and exit with failure.
+ */
+void
+usage_fail(const char *name)
+{
+
+ const char *header =
+ "usage: %s [options] <file.vert | file.geom | file.frag>\n"
+ "\n"
+ "Possible options are:\n";
+ printf(header, name, name);
+ for (const struct option *o = compiler_opts; o->name != 0; ++o) {
+ printf(" --%s\n", o->name);
+ }
+ exit(EXIT_FAILURE);
+}
+
+
+void
+compile_shader(struct gl_context *ctx, struct gl_shader *shader)
+{
+ struct _mesa_glsl_parse_state *state =
+ new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
+
+ const char *source = shader->Source;
+ state->error = preprocess(state, &source, &state->info_log,
+ state->extensions, ctx->API) != 0;
+
+ if (!state->error) {
+ _mesa_glsl_lexer_ctor(state, source);
+ _mesa_glsl_parse(state);
+ _mesa_glsl_lexer_dtor(state);
+ }
+
+ if (dump_ast) {
+ foreach_list_const(n, &state->translation_unit) {
+ ast_node *ast = exec_node_data(ast_node, n, link);
+ ast->print();
+ }
+ printf("\n\n");
+ }
+
+ shader->ir = new(shader) exec_list;
+ if (!state->error && !state->translation_unit.is_empty())
+ _mesa_ast_to_hir(shader->ir, state);
+
+ /* Print out the unoptimized IR. */
+ if (!state->error && dump_hir) {
+ validate_ir_tree(shader->ir);
+ _mesa_print_ir(shader->ir, state);
+ }
+
+ /* Optimization passes */
+ if (!state->error && !shader->ir->is_empty()) {
+ bool progress;
+ do {
+ progress = do_common_optimization(shader->ir, false, 32);
+ } while (progress);
+
+ validate_ir_tree(shader->ir);
+ }
+
+
+ /* Print out the resulting IR */
+ if (!state->error && dump_lir) {
+ _mesa_print_ir(shader->ir, state);
+ }
+
+ shader->symbols = state->symbols;
+ shader->CompileStatus = !state->error;
+ shader->Version = state->language_version;
+ memcpy(shader->builtins_to_link, state->builtins_to_link,
+ sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link);
+ shader->num_builtins_to_link = state->num_builtins_to_link;
+
+ if (shader->InfoLog)
+ ralloc_free(shader->InfoLog);
+
+ shader->InfoLog = state->info_log;
+
+ /* Retain any live IR, but trash the rest. */
+ reparent_ir(shader->ir, shader);
+
+ ralloc_free(state);
+
+ return;
+}
+
+int
+main(int argc, char **argv)
+{
+ int status = EXIT_SUCCESS;
+ struct gl_context local_ctx;
+ struct gl_context *ctx = &local_ctx;
+
+ int c;
+ int idx = 0;
+ while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1)
+ /* empty */ ;
+
+
+ if (argc <= optind)
+ usage_fail(argv[0]);
+
+ initialize_context(ctx, (glsl_es) ? API_OPENGLES2 : API_OPENGL);
+
+ struct gl_shader_program *whole_program;
+
+ whole_program = rzalloc (NULL, struct gl_shader_program);
+ assert(whole_program != NULL);
+
+ for (/* empty */; argc > optind; optind++) {
+ whole_program->Shaders =
+ reralloc(whole_program, whole_program->Shaders,
+ struct gl_shader *, whole_program->NumShaders + 1);
+ assert(whole_program->Shaders != NULL);
+
+ struct gl_shader *shader = rzalloc(whole_program, gl_shader);
+
+ whole_program->Shaders[whole_program->NumShaders] = shader;
+ whole_program->NumShaders++;
+
+ const unsigned len = strlen(argv[optind]);
+ if (len < 6)
+ usage_fail(argv[0]);
+
+ const char *const ext = & argv[optind][len - 5];
+ if (strncmp(".vert", ext, 5) == 0)
+ shader->Type = GL_VERTEX_SHADER;
+ else if (strncmp(".geom", ext, 5) == 0)
+ shader->Type = GL_GEOMETRY_SHADER;
+ else if (strncmp(".frag", ext, 5) == 0)
+ shader->Type = GL_FRAGMENT_SHADER;
+ else
+ usage_fail(argv[0]);
+
+ shader->Source = load_text_file(whole_program, argv[optind]);
+ if (shader->Source == NULL) {
+ printf("File \"%s\" does not exist.\n", argv[optind]);
+ exit(EXIT_FAILURE);
+ }
+
+ compile_shader(ctx, shader);
+
+ if (!shader->CompileStatus) {
+ printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog);
+ status = EXIT_FAILURE;
+ break;
+ }
+ }
+
+ if ((status == EXIT_SUCCESS) && do_link) {
+ link_shaders(ctx, whole_program);
+ status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+ if (strlen(whole_program->InfoLog) > 0)
+ printf("Info log for linking:\n%s\n", whole_program->InfoLog);
+ }
+
+ for (unsigned i = 0; i < MESA_SHADER_TYPES; i++)
+ ralloc_free(whole_program->_LinkedShaders[i]);
+
+ ralloc_free(whole_program);
+ _mesa_glsl_release_types();
+ _mesa_glsl_release_functions();
+
+ return status;
+}
diff --git a/mesalib/src/glsl/opt_constant_propagation.cpp b/mesalib/src/glsl/opt_constant_propagation.cpp index 4425f4211..55c271200 100644 --- a/mesalib/src/glsl/opt_constant_propagation.cpp +++ b/mesalib/src/glsl/opt_constant_propagation.cpp @@ -1,434 +1,434 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * constant of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, constant, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above constantright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR CONSTANTRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_constant_propagation.cpp - * - * Tracks assignments of constants to channels of variables, and - * usage of those constant channels with direct usage of the constants. - * - * This can lead to constant folding and algebraic optimizations in - * those later expressions, while causing no increase in instruction - * count (due to constants being generally free to load from a - * constant push buffer or as instruction immediate values) and - * possibly reducing register pressure. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_rvalue_visitor.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -class acp_entry : public exec_node -{ -public: - acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant) - { - assert(var); - assert(constant); - this->var = var; - this->write_mask = write_mask; - this->constant = constant; - } - - ir_variable *var; - ir_constant *constant; - unsigned write_mask; -}; - - -class kill_entry : public exec_node -{ -public: - kill_entry(ir_variable *var, unsigned write_mask) - { - assert(var); - this->var = var; - this->write_mask = write_mask; - } - - ir_variable *var; - unsigned write_mask; -}; - -class ir_constant_propagation_visitor : public ir_rvalue_visitor { -public: - ir_constant_propagation_visitor() - { - progress = false; - mem_ctx = ralloc_context(0); - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - } - ~ir_constant_propagation_visitor() - { - ralloc_free(mem_ctx); - } - - virtual ir_visitor_status visit_enter(class ir_loop *); - virtual ir_visitor_status visit_enter(class ir_function_signature *); - virtual ir_visitor_status visit_enter(class ir_function *); - virtual ir_visitor_status visit_leave(class ir_assignment *); - virtual ir_visitor_status visit_enter(class ir_call *); - virtual ir_visitor_status visit_enter(class ir_if *); - - void add_constant(ir_assignment *ir); - void kill(ir_variable *ir, unsigned write_mask); - void handle_if_block(exec_list *instructions); - void handle_rvalue(ir_rvalue **rvalue); - - /** List of acp_entry: The available constants to propagate */ - exec_list *acp; - - /** - * List of kill_entry: The masks of variables whose values were - * killed in this block. - */ - exec_list *kills; - - bool progress; - - bool killed_all; - - void *mem_ctx; -}; - - -void -ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue) -{ - if (this->in_assignee || !*rvalue) - return; - - const glsl_type *type = (*rvalue)->type; - if (!type->is_scalar() && !type->is_vector()) - return; - - ir_swizzle *swiz = NULL; - ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); - if (!deref) { - swiz = (*rvalue)->as_swizzle(); - if (!swiz) - return; - - deref = swiz->val->as_dereference_variable(); - if (!deref) - return; - } - - ir_constant_data data; - memset(&data, 0, sizeof(data)); - - for (unsigned int i = 0; i < type->components(); i++) { - int channel; - acp_entry *found = NULL; - - if (swiz) { - switch (i) { - case 0: channel = swiz->mask.x; break; - case 1: channel = swiz->mask.y; break; - case 2: channel = swiz->mask.z; break; - case 3: channel = swiz->mask.w; break; - default: assert(!"shouldn't be reached"); channel = 0; break; - } - } else { - channel = i; - } - - foreach_iter(exec_list_iterator, iter, *this->acp) { - acp_entry *entry = (acp_entry *)iter.get(); - if (entry->var == deref->var && entry->write_mask & (1 << channel)) { - found = entry; - break; - } - } - - if (!found) - return; - - int rhs_channel = 0; - for (int j = 0; j < 4; j++) { - if (j == channel) - break; - if (found->write_mask & (1 << j)) - rhs_channel++; - } - - switch (type->base_type) { - case GLSL_TYPE_FLOAT: - data.f[i] = found->constant->value.f[rhs_channel]; - break; - case GLSL_TYPE_INT: - data.i[i] = found->constant->value.i[rhs_channel]; - break; - case GLSL_TYPE_UINT: - data.u[i] = found->constant->value.u[rhs_channel]; - break; - case GLSL_TYPE_BOOL: - data.b[i] = found->constant->value.b[rhs_channel]; - break; - default: - assert(!"not reached"); - break; - } - } - - *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data); - this->progress = true; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir) -{ - /* Treat entry into a function signature as a completely separate - * block. Any instructions at global scope will be shuffled into - * main() at link time, so they're irrelevant to us. - */ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body); - - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = orig_killed_all; - - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_leave(ir_assignment *ir) -{ - if (this->in_assignee) - return visit_continue; - - kill(ir->lhs->variable_referenced(), ir->write_mask); - - add_constant(ir); - - return visit_continue; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_function *ir) -{ - (void) ir; - return visit_continue; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_call *ir) -{ - /* Do constant propagation on call parameters, but skip any out params */ - exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, ir->actual_parameters) { - ir_variable *sig_param = (ir_variable *)sig_param_iter.get(); - ir_rvalue *param = (ir_rvalue *)iter.get(); - if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) { - ir_rvalue *new_param = param; - handle_rvalue(&new_param); - if (new_param != param) - param->replace_with(new_param); - else - param->accept(this); - } - sig_param_iter.next(); - } - - /* Since we're unlinked, we don't (necssarily) know the side effects of - * this call. So kill all copies. - */ - acp->make_empty(); - this->killed_all = true; - - return visit_continue_with_parent; -} - -void -ir_constant_propagation_visitor::handle_if_block(exec_list *instructions) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - /* Populate the initial acp with a constant of the original */ - foreach_iter(exec_list_iterator, iter, *orig_acp) { - acp_entry *a = (acp_entry *)iter.get(); - this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask, - a->constant)); - } - - visit_list_elements(this, instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - foreach_iter(exec_list_iterator, iter, *new_kills) { - kill_entry *k = (kill_entry *)iter.get(); - kill(k->var, k->write_mask); - } -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_if *ir) -{ - ir->condition->accept(this); - handle_rvalue(&ir->condition); - - handle_if_block(&ir->then_instructions); - handle_if_block(&ir->else_instructions); - - /* handle_if_block() already descended into the children. */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_propagation_visitor::visit_enter(ir_loop *ir) -{ - exec_list *orig_acp = this->acp; - exec_list *orig_kills = this->kills; - bool orig_killed_all = this->killed_all; - - /* FINISHME: For now, the initial acp for loops is totally empty. - * We could go through once, then go through again with the acp - * cloned minus the killed entries after the first run through. - */ - this->acp = new(mem_ctx) exec_list; - this->kills = new(mem_ctx) exec_list; - this->killed_all = false; - - visit_list_elements(this, &ir->body_instructions); - - if (this->killed_all) { - orig_acp->make_empty(); - } - - exec_list *new_kills = this->kills; - this->kills = orig_kills; - this->acp = orig_acp; - this->killed_all = this->killed_all || orig_killed_all; - - foreach_iter(exec_list_iterator, iter, *new_kills) { - kill_entry *k = (kill_entry *)iter.get(); - kill(k->var, k->write_mask); - } - - /* already descended into the children. */ - return visit_continue_with_parent; -} - -void -ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask) -{ - assert(var != NULL); - - /* We don't track non-vectors. */ - if (!var->type->is_vector() && !var->type->is_scalar()) - return; - - /* Remove any entries currently in the ACP for this kill. */ - foreach_iter(exec_list_iterator, iter, *this->acp) { - acp_entry *entry = (acp_entry *)iter.get(); - - if (entry->var == var) { - entry->write_mask &= ~write_mask; - if (entry->write_mask == 0) - entry->remove(); - } - } - - /* Add this writemask of the variable to the list of killed - * variables in this block. - */ - foreach_iter(exec_list_iterator, iter, *this->kills) { - kill_entry *entry = (kill_entry *)iter.get(); - - if (entry->var == var) { - entry->write_mask |= write_mask; - return; - } - } - /* Not already in the list. Make new entry. */ - this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask)); -} - -/** - * Adds an entry to the available constant list if it's a plain assignment - * of a variable to a variable. - */ -void -ir_constant_propagation_visitor::add_constant(ir_assignment *ir) -{ - acp_entry *entry; - - if (ir->condition) - return; - - if (!ir->write_mask) - return; - - ir_dereference_variable *deref = ir->lhs->as_dereference_variable(); - ir_constant *constant = ir->rhs->as_constant(); - - if (!deref || !constant) - return; - - /* Only do constant propagation on vectors. Constant matrices, - * arrays, or structures would require more work elsewhere. - */ - if (!deref->var->type->is_vector() && !deref->var->type->is_scalar()) - return; - - entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant); - this->acp->push_tail(entry); -} - -/** - * Does a constant propagation pass on the code present in the instruction stream. - */ -bool -do_constant_propagation(exec_list *instructions) -{ - ir_constant_propagation_visitor v; - - visit_list_elements(&v, instructions); - - return v.progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * constant of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, constant, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above constantright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR CONSTANTRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_constant_propagation.cpp
+ *
+ * Tracks assignments of constants to channels of variables, and
+ * usage of those constant channels with direct usage of the constants.
+ *
+ * This can lead to constant folding and algebraic optimizations in
+ * those later expressions, while causing no increase in instruction
+ * count (due to constants being generally free to load from a
+ * constant push buffer or as instruction immediate values) and
+ * possibly reducing register pressure.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_rvalue_visitor.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+class acp_entry : public exec_node
+{
+public:
+ acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant)
+ {
+ assert(var);
+ assert(constant);
+ this->var = var;
+ this->write_mask = write_mask;
+ this->constant = constant;
+ }
+
+ ir_variable *var;
+ ir_constant *constant;
+ unsigned write_mask;
+};
+
+
+class kill_entry : public exec_node
+{
+public:
+ kill_entry(ir_variable *var, unsigned write_mask)
+ {
+ assert(var);
+ this->var = var;
+ this->write_mask = write_mask;
+ }
+
+ ir_variable *var;
+ unsigned write_mask;
+};
+
+class ir_constant_propagation_visitor : public ir_rvalue_visitor {
+public:
+ ir_constant_propagation_visitor()
+ {
+ progress = false;
+ mem_ctx = ralloc_context(0);
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ }
+ ~ir_constant_propagation_visitor()
+ {
+ ralloc_free(mem_ctx);
+ }
+
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_leave(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
+
+ void add_constant(ir_assignment *ir);
+ void kill(ir_variable *ir, unsigned write_mask);
+ void handle_if_block(exec_list *instructions);
+ void handle_rvalue(ir_rvalue **rvalue);
+
+ /** List of acp_entry: The available constants to propagate */
+ exec_list *acp;
+
+ /**
+ * List of kill_entry: The masks of variables whose values were
+ * killed in this block.
+ */
+ exec_list *kills;
+
+ bool progress;
+
+ bool killed_all;
+
+ void *mem_ctx;
+};
+
+
+void
+ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue)
+{
+ if (this->in_assignee || !*rvalue)
+ return;
+
+ const glsl_type *type = (*rvalue)->type;
+ if (!type->is_scalar() && !type->is_vector())
+ return;
+
+ ir_swizzle *swiz = NULL;
+ ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
+ if (!deref) {
+ swiz = (*rvalue)->as_swizzle();
+ if (!swiz)
+ return;
+
+ deref = swiz->val->as_dereference_variable();
+ if (!deref)
+ return;
+ }
+
+ ir_constant_data data;
+ memset(&data, 0, sizeof(data));
+
+ for (unsigned int i = 0; i < type->components(); i++) {
+ int channel;
+ acp_entry *found = NULL;
+
+ if (swiz) {
+ switch (i) {
+ case 0: channel = swiz->mask.x; break;
+ case 1: channel = swiz->mask.y; break;
+ case 2: channel = swiz->mask.z; break;
+ case 3: channel = swiz->mask.w; break;
+ default: assert(!"shouldn't be reached"); channel = 0; break;
+ }
+ } else {
+ channel = i;
+ }
+
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+ if (entry->var == deref->var && entry->write_mask & (1 << channel)) {
+ found = entry;
+ break;
+ }
+ }
+
+ if (!found)
+ return;
+
+ int rhs_channel = 0;
+ for (int j = 0; j < 4; j++) {
+ if (j == channel)
+ break;
+ if (found->write_mask & (1 << j))
+ rhs_channel++;
+ }
+
+ switch (type->base_type) {
+ case GLSL_TYPE_FLOAT:
+ data.f[i] = found->constant->value.f[rhs_channel];
+ break;
+ case GLSL_TYPE_INT:
+ data.i[i] = found->constant->value.i[rhs_channel];
+ break;
+ case GLSL_TYPE_UINT:
+ data.u[i] = found->constant->value.u[rhs_channel];
+ break;
+ case GLSL_TYPE_BOOL:
+ data.b[i] = found->constant->value.b[rhs_channel];
+ break;
+ default:
+ assert(!"not reached");
+ break;
+ }
+ }
+
+ *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data);
+ this->progress = true;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)
+{
+ /* Treat entry into a function signature as a completely separate
+ * block. Any instructions at global scope will be shuffled into
+ * main() at link time, so they're irrelevant to us.
+ */
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body);
+
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = orig_killed_all;
+
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_leave(ir_assignment *ir)
+{
+ if (this->in_assignee)
+ return visit_continue;
+
+ kill(ir->lhs->variable_referenced(), ir->write_mask);
+
+ add_constant(ir);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_function *ir)
+{
+ (void) ir;
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_call *ir)
+{
+ /* Do constant propagation on call parameters, but skip any out params */
+ exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
+ ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
+ ir_rvalue *new_param = param;
+ handle_rvalue(&new_param);
+ if (new_param != param)
+ param->replace_with(new_param);
+ else
+ param->accept(this);
+ }
+ sig_param_iter.next();
+ }
+
+ /* Since we're unlinked, we don't (necssarily) know the side effects of
+ * this call. So kill all copies.
+ */
+ acp->make_empty();
+ this->killed_all = true;
+
+ return visit_continue_with_parent;
+}
+
+void
+ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ /* Populate the initial acp with a constant of the original */
+ foreach_iter(exec_list_iterator, iter, *orig_acp) {
+ acp_entry *a = (acp_entry *)iter.get();
+ this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask,
+ a->constant));
+ }
+
+ visit_list_elements(this, instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var, k->write_mask);
+ }
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_if *ir)
+{
+ ir->condition->accept(this);
+ handle_rvalue(&ir->condition);
+
+ handle_if_block(&ir->then_instructions);
+ handle_if_block(&ir->else_instructions);
+
+ /* handle_if_block() already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_propagation_visitor::visit_enter(ir_loop *ir)
+{
+ exec_list *orig_acp = this->acp;
+ exec_list *orig_kills = this->kills;
+ bool orig_killed_all = this->killed_all;
+
+ /* FINISHME: For now, the initial acp for loops is totally empty.
+ * We could go through once, then go through again with the acp
+ * cloned minus the killed entries after the first run through.
+ */
+ this->acp = new(mem_ctx) exec_list;
+ this->kills = new(mem_ctx) exec_list;
+ this->killed_all = false;
+
+ visit_list_elements(this, &ir->body_instructions);
+
+ if (this->killed_all) {
+ orig_acp->make_empty();
+ }
+
+ exec_list *new_kills = this->kills;
+ this->kills = orig_kills;
+ this->acp = orig_acp;
+ this->killed_all = this->killed_all || orig_killed_all;
+
+ foreach_iter(exec_list_iterator, iter, *new_kills) {
+ kill_entry *k = (kill_entry *)iter.get();
+ kill(k->var, k->write_mask);
+ }
+
+ /* already descended into the children. */
+ return visit_continue_with_parent;
+}
+
+void
+ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask)
+{
+ assert(var != NULL);
+
+ /* We don't track non-vectors. */
+ if (!var->type->is_vector() && !var->type->is_scalar())
+ return;
+
+ /* Remove any entries currently in the ACP for this kill. */
+ foreach_iter(exec_list_iterator, iter, *this->acp) {
+ acp_entry *entry = (acp_entry *)iter.get();
+
+ if (entry->var == var) {
+ entry->write_mask &= ~write_mask;
+ if (entry->write_mask == 0)
+ entry->remove();
+ }
+ }
+
+ /* Add this writemask of the variable to the list of killed
+ * variables in this block.
+ */
+ foreach_iter(exec_list_iterator, iter, *this->kills) {
+ kill_entry *entry = (kill_entry *)iter.get();
+
+ if (entry->var == var) {
+ entry->write_mask |= write_mask;
+ return;
+ }
+ }
+ /* Not already in the list. Make new entry. */
+ this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask));
+}
+
+/**
+ * Adds an entry to the available constant list if it's a plain assignment
+ * of a variable to a variable.
+ */
+void
+ir_constant_propagation_visitor::add_constant(ir_assignment *ir)
+{
+ acp_entry *entry;
+
+ if (ir->condition)
+ return;
+
+ if (!ir->write_mask)
+ return;
+
+ ir_dereference_variable *deref = ir->lhs->as_dereference_variable();
+ ir_constant *constant = ir->rhs->as_constant();
+
+ if (!deref || !constant)
+ return;
+
+ /* Only do constant propagation on vectors. Constant matrices,
+ * arrays, or structures would require more work elsewhere.
+ */
+ if (!deref->var->type->is_vector() && !deref->var->type->is_scalar())
+ return;
+
+ entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant);
+ this->acp->push_tail(entry);
+}
+
+/**
+ * Does a constant propagation pass on the code present in the instruction stream.
+ */
+bool
+do_constant_propagation(exec_list *instructions)
+{
+ ir_constant_propagation_visitor v;
+
+ visit_list_elements(&v, instructions);
+
+ return v.progress;
+}
diff --git a/mesalib/src/glsl/opt_constant_variable.cpp b/mesalib/src/glsl/opt_constant_variable.cpp index 3fa7c3bad..f1aeaa99a 100644 --- a/mesalib/src/glsl/opt_constant_variable.cpp +++ b/mesalib/src/glsl/opt_constant_variable.cpp @@ -1,195 +1,195 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_constant_variable.cpp - * - * Marks variables assigned a single constant value over the course - * of the program as constant. - * - * The goal here is to trigger further constant folding and then dead - * code elimination. This is common with vector/matrix constructors - * and calls to builtin functions. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -struct assignment_entry { - exec_node link; - int assignment_count; - ir_variable *var; - ir_constant *constval; - bool our_scope; -}; - -class ir_constant_variable_visitor : public ir_hierarchical_visitor { -public: - virtual ir_visitor_status visit_enter(ir_dereference_variable *); - virtual ir_visitor_status visit(ir_variable *); - virtual ir_visitor_status visit_enter(ir_assignment *); - virtual ir_visitor_status visit_enter(ir_call *); - - exec_list list; -}; - -static struct assignment_entry * -get_assignment_entry(ir_variable *var, exec_list *list) -{ - struct assignment_entry *entry; - - foreach_list_typed(struct assignment_entry, entry, link, list) { - if (entry->var == var) - return entry; - } - - entry = (struct assignment_entry *)calloc(1, sizeof(*entry)); - entry->var = var; - list->push_head(&entry->link); - return entry; -} - -ir_visitor_status -ir_constant_variable_visitor::visit(ir_variable *ir) -{ - struct assignment_entry *entry = get_assignment_entry(ir, &this->list); - entry->our_scope = true; - return visit_continue; -} - -/* Skip derefs of variables so that we can detect declarations. */ -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir) -{ - (void)ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_assignment *ir) -{ - ir_constant *constval; - struct assignment_entry *entry; - - entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list); - assert(entry); - entry->assignment_count++; - - /* If it's already constant, don't do the work. */ - if (entry->var->constant_value) - return visit_continue; - - /* OK, now find if we actually have all the right conditions for - * this to be a constant value assigned to the var. - */ - if (ir->condition) - return visit_continue; - - ir_variable *var = ir->whole_variable_written(); - if (!var) - return visit_continue; - - constval = ir->rhs->constant_expression_value(); - if (!constval) - return visit_continue; - - /* Mark this entry as having a constant assignment (if the - * assignment count doesn't go >1). do_constant_variable will fix - * up the variable with the constant value later. - */ - entry->constval = constval; - - return visit_continue; -} - -ir_visitor_status -ir_constant_variable_visitor::visit_enter(ir_call *ir) -{ - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param_rval = (ir_rvalue *)iter.get(); - ir_variable *param = (ir_variable *)sig_iter.get(); - - if (param->mode == ir_var_out || - param->mode == ir_var_inout) { - ir_variable *var = param_rval->variable_referenced(); - struct assignment_entry *entry; - - assert(var); - entry = get_assignment_entry(var, &this->list); - entry->assignment_count++; - } - sig_iter.next(); - } - return visit_continue; -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_constant_variable(exec_list *instructions) -{ - bool progress = false; - ir_constant_variable_visitor v; - - v.run(instructions); - - while (!v.list.is_empty()) { - - struct assignment_entry *entry; - entry = exec_node_data(struct assignment_entry, v.list.head, link); - - if (entry->assignment_count == 1 && entry->constval && entry->our_scope) { - entry->var->constant_value = entry->constval; - progress = true; - } - entry->link.remove(); - free(entry); - } - - return progress; -} - -bool -do_constant_variable_unlinked(exec_list *instructions) -{ - bool progress = false; - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_function *f = ir->as_function(); - if (f) { - foreach_iter(exec_list_iterator, sigiter, *f) { - ir_function_signature *sig = - (ir_function_signature *) sigiter.get(); - if (do_constant_variable(&sig->body)) - progress = true; - } - } - } - - return progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_constant_variable.cpp
+ *
+ * Marks variables assigned a single constant value over the course
+ * of the program as constant.
+ *
+ * The goal here is to trigger further constant folding and then dead
+ * code elimination. This is common with vector/matrix constructors
+ * and calls to builtin functions.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+struct assignment_entry {
+ exec_node link;
+ int assignment_count;
+ ir_variable *var;
+ ir_constant *constval;
+ bool our_scope;
+};
+
+class ir_constant_variable_visitor : public ir_hierarchical_visitor {
+public:
+ virtual ir_visitor_status visit_enter(ir_dereference_variable *);
+ virtual ir_visitor_status visit(ir_variable *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+
+ exec_list list;
+};
+
+static struct assignment_entry *
+get_assignment_entry(ir_variable *var, exec_list *list)
+{
+ struct assignment_entry *entry;
+
+ foreach_list_typed(struct assignment_entry, entry, link, list) {
+ if (entry->var == var)
+ return entry;
+ }
+
+ entry = (struct assignment_entry *)calloc(1, sizeof(*entry));
+ entry->var = var;
+ list->push_head(&entry->link);
+ return entry;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit(ir_variable *ir)
+{
+ struct assignment_entry *entry = get_assignment_entry(ir, &this->list);
+ entry->our_scope = true;
+ return visit_continue;
+}
+
+/* Skip derefs of variables so that we can detect declarations. */
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
+{
+ (void)ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_assignment *ir)
+{
+ ir_constant *constval;
+ struct assignment_entry *entry;
+
+ entry = get_assignment_entry(ir->lhs->variable_referenced(), &this->list);
+ assert(entry);
+ entry->assignment_count++;
+
+ /* If it's already constant, don't do the work. */
+ if (entry->var->constant_value)
+ return visit_continue;
+
+ /* OK, now find if we actually have all the right conditions for
+ * this to be a constant value assigned to the var.
+ */
+ if (ir->condition)
+ return visit_continue;
+
+ ir_variable *var = ir->whole_variable_written();
+ if (!var)
+ return visit_continue;
+
+ constval = ir->rhs->constant_expression_value();
+ if (!constval)
+ return visit_continue;
+
+ /* Mark this entry as having a constant assignment (if the
+ * assignment count doesn't go >1). do_constant_variable will fix
+ * up the variable with the constant value later.
+ */
+ entry->constval = constval;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_constant_variable_visitor::visit_enter(ir_call *ir)
+{
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param_rval = (ir_rvalue *)iter.get();
+ ir_variable *param = (ir_variable *)sig_iter.get();
+
+ if (param->mode == ir_var_out ||
+ param->mode == ir_var_inout) {
+ ir_variable *var = param_rval->variable_referenced();
+ struct assignment_entry *entry;
+
+ assert(var);
+ entry = get_assignment_entry(var, &this->list);
+ entry->assignment_count++;
+ }
+ sig_iter.next();
+ }
+ return visit_continue;
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_constant_variable(exec_list *instructions)
+{
+ bool progress = false;
+ ir_constant_variable_visitor v;
+
+ v.run(instructions);
+
+ while (!v.list.is_empty()) {
+
+ struct assignment_entry *entry;
+ entry = exec_node_data(struct assignment_entry, v.list.head, link);
+
+ if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
+ entry->var->constant_value = entry->constval;
+ progress = true;
+ }
+ entry->link.remove();
+ free(entry);
+ }
+
+ return progress;
+}
+
+bool
+do_constant_variable_unlinked(exec_list *instructions)
+{
+ bool progress = false;
+
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_function *f = ir->as_function();
+ if (f) {
+ foreach_iter(exec_list_iterator, sigiter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) sigiter.get();
+ if (do_constant_variable(&sig->body))
+ progress = true;
+ }
+ }
+ }
+
+ return progress;
+}
diff --git a/mesalib/src/glsl/opt_dead_code.cpp b/mesalib/src/glsl/opt_dead_code.cpp index cb500d2d1..492ba73a1 100644 --- a/mesalib/src/glsl/opt_dead_code.cpp +++ b/mesalib/src/glsl/opt_dead_code.cpp @@ -1,142 +1,142 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_dead_code.cpp - * - * Eliminates dead assignments and variable declarations from the code. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_variable_refcount.h" -#include "glsl_types.h" - -static bool debug = false; - -/** - * Do a dead code pass over instructions and everything that instructions - * references. - * - * Note that this will remove assignments to globals, so it is not suitable - * for usage on an unlinked instruction stream. - */ -bool -do_dead_code(exec_list *instructions) -{ - ir_variable_refcount_visitor v; - bool progress = false; - - v.run(instructions); - - foreach_iter(exec_list_iterator, iter, v.variable_list) { - variable_entry *entry = (variable_entry *)iter.get(); - - /* Since each assignment is a reference, the refereneced count must be - * greater than or equal to the assignment count. If they are equal, - * then all of the references are assignments, and the variable is - * dead. - * - * Note that if the variable is neither assigned nor referenced, both - * counts will be zero and will be caught by the equality test. - */ - assert(entry->referenced_count >= entry->assigned_count); - - if (debug) { - printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n", - entry->var->name, (void *) entry->var, - entry->referenced_count, entry->assigned_count, - entry->declaration ? "" : "not "); - } - - if ((entry->referenced_count > entry->assigned_count) - || !entry->declaration) - continue; - - if (entry->assign) { - /* Remove a single dead assignment to the variable we found. - * Don't do so if it's a shader output, though. - */ - if (entry->var->mode != ir_var_out && - entry->var->mode != ir_var_inout && - !ir_has_call(entry->assign)) { - entry->assign->remove(); - progress = true; - - if (debug) { - printf("Removed assignment to %s@%p\n", - entry->var->name, (void *) entry->var); - } - } - } else { - /* If there are no assignments or references to the variable left, - * then we can remove its declaration. - */ - - /* uniform initializers are precious, and could get used by another - * stage. - */ - if (entry->var->mode == ir_var_uniform && - entry->var->constant_value) - continue; - - entry->var->remove(); - progress = true; - - if (debug) { - printf("Removed declaration of %s@%p\n", - entry->var->name, (void *) entry->var); - } - } - } - - return progress; -} - -/** - * Does a dead code pass on the functions present in the instruction stream. - * - * This is suitable for use while the program is not linked, as it will - * ignore variable declarations (and the assignments to them) for variables - * with global scope. - */ -bool -do_dead_code_unlinked(exec_list *instructions) -{ - bool progress = false; - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_function *f = ir->as_function(); - if (f) { - foreach_iter(exec_list_iterator, sigiter, *f) { - ir_function_signature *sig = - (ir_function_signature *) sigiter.get(); - if (do_dead_code(&sig->body)) - progress = true; - } - } - } - - return progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_dead_code.cpp
+ *
+ * Eliminates dead assignments and variable declarations from the code.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_variable_refcount.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+/**
+ * Do a dead code pass over instructions and everything that instructions
+ * references.
+ *
+ * Note that this will remove assignments to globals, so it is not suitable
+ * for usage on an unlinked instruction stream.
+ */
+bool
+do_dead_code(exec_list *instructions)
+{
+ ir_variable_refcount_visitor v;
+ bool progress = false;
+
+ v.run(instructions);
+
+ foreach_iter(exec_list_iterator, iter, v.variable_list) {
+ variable_entry *entry = (variable_entry *)iter.get();
+
+ /* Since each assignment is a reference, the refereneced count must be
+ * greater than or equal to the assignment count. If they are equal,
+ * then all of the references are assignments, and the variable is
+ * dead.
+ *
+ * Note that if the variable is neither assigned nor referenced, both
+ * counts will be zero and will be caught by the equality test.
+ */
+ assert(entry->referenced_count >= entry->assigned_count);
+
+ if (debug) {
+ printf("%s@%p: %d refs, %d assigns, %sdeclared in our scope\n",
+ entry->var->name, (void *) entry->var,
+ entry->referenced_count, entry->assigned_count,
+ entry->declaration ? "" : "not ");
+ }
+
+ if ((entry->referenced_count > entry->assigned_count)
+ || !entry->declaration)
+ continue;
+
+ if (entry->assign) {
+ /* Remove a single dead assignment to the variable we found.
+ * Don't do so if it's a shader output, though.
+ */
+ if (entry->var->mode != ir_var_out &&
+ entry->var->mode != ir_var_inout &&
+ !ir_has_call(entry->assign)) {
+ entry->assign->remove();
+ progress = true;
+
+ if (debug) {
+ printf("Removed assignment to %s@%p\n",
+ entry->var->name, (void *) entry->var);
+ }
+ }
+ } else {
+ /* If there are no assignments or references to the variable left,
+ * then we can remove its declaration.
+ */
+
+ /* uniform initializers are precious, and could get used by another
+ * stage.
+ */
+ if (entry->var->mode == ir_var_uniform &&
+ entry->var->constant_value)
+ continue;
+
+ entry->var->remove();
+ progress = true;
+
+ if (debug) {
+ printf("Removed declaration of %s@%p\n",
+ entry->var->name, (void *) entry->var);
+ }
+ }
+ }
+
+ return progress;
+}
+
+/**
+ * Does a dead code pass on the functions present in the instruction stream.
+ *
+ * This is suitable for use while the program is not linked, as it will
+ * ignore variable declarations (and the assignments to them) for variables
+ * with global scope.
+ */
+bool
+do_dead_code_unlinked(exec_list *instructions)
+{
+ bool progress = false;
+
+ foreach_iter(exec_list_iterator, iter, *instructions) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_function *f = ir->as_function();
+ if (f) {
+ foreach_iter(exec_list_iterator, sigiter, *f) {
+ ir_function_signature *sig =
+ (ir_function_signature *) sigiter.get();
+ if (do_dead_code(&sig->body))
+ progress = true;
+ }
+ }
+ }
+
+ return progress;
+}
diff --git a/mesalib/src/glsl/opt_dead_code_local.cpp b/mesalib/src/glsl/opt_dead_code_local.cpp index 39962bd60..42e5e94c8 100644 --- a/mesalib/src/glsl/opt_dead_code_local.cpp +++ b/mesalib/src/glsl/opt_dead_code_local.cpp @@ -1,222 +1,222 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_dead_code_local.cpp - * - * Eliminates local dead assignments from the code. - * - * This operates on basic blocks, tracking assignments and finding if - * they're used before the variable is completely reassigned. - * - * Compare this to ir_dead_code.cpp, which operates globally looking - * for assignments to variables that are never read. - */ - -#include "ir.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -static bool debug = false; - -class assignment_entry : public exec_node -{ -public: - assignment_entry(ir_variable *lhs, ir_instruction *ir) - { - assert(lhs); - assert(ir); - this->lhs = lhs; - this->ir = ir; - } - - ir_variable *lhs; - ir_instruction *ir; -}; - -class kill_for_derefs_visitor : public ir_hierarchical_visitor { -public: - kill_for_derefs_visitor(exec_list *assignments) - { - this->assignments = assignments; - } - - virtual ir_visitor_status visit(ir_dereference_variable *ir) - { - ir_variable *const var = ir->variable_referenced(); - - foreach_iter(exec_list_iterator, iter, *this->assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - if (entry->lhs == var) { - if (debug) - printf("kill %s\n", entry->lhs->name); - entry->remove(); - } - } - - return visit_continue; - } - -private: - exec_list *assignments; -}; - -class array_index_visit : public ir_hierarchical_visitor { -public: - array_index_visit(ir_hierarchical_visitor *v) - { - this->visitor = v; - } - - virtual ir_visitor_status visit_enter(class ir_dereference_array *ir) - { - ir->array_index->accept(visitor); - return visit_continue; - } - - static void run(ir_instruction *ir, ir_hierarchical_visitor *v) - { - array_index_visit top_visit(v); - ir->accept(& top_visit); - } - - ir_hierarchical_visitor *visitor; -}; - - -/** - * Adds an entry to the available copy list if it's a plain assignment - * of a variable to a variable. - */ -static bool -process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments) -{ - ir_variable *var = NULL; - bool progress = false; - kill_for_derefs_visitor v(assignments); - - /* Kill assignment entries for things used to produce this assignment. */ - ir->rhs->accept(&v); - if (ir->condition) { - ir->condition->accept(&v); - } - - /* Kill assignment enties used as array indices. - */ - array_index_visit::run(ir->lhs, &v); - var = ir->lhs->variable_referenced(); - assert(var); - - /* Now, check if we did a whole-variable assignment. */ - if (!ir->condition && (ir->whole_variable_written() != NULL)) { - /* We did a whole-variable assignment. So, any instruction in - * the assignment list with the same LHS is dead. - */ - if (debug) - printf("looking for %s to remove\n", var->name); - foreach_iter(exec_list_iterator, iter, *assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - if (entry->lhs == var) { - if (debug) - printf("removing %s\n", var->name); - entry->ir->remove(); - entry->remove(); - progress = true; - } - } - } - - /* Add this instruction to the assignment list available to be removed. - * But not if the assignment has other side effects. - */ - if (ir_has_call(ir)) - return progress; - - assignment_entry *entry = new(ctx) assignment_entry(var, ir); - assignments->push_tail(entry); - - if (debug) { - printf("add %s\n", var->name); - - printf("current entries\n"); - foreach_iter(exec_list_iterator, iter, *assignments) { - assignment_entry *entry = (assignment_entry *)iter.get(); - - printf(" %s\n", entry->lhs->name); - } - } - - return progress; -} - -static void -dead_code_local_basic_block(ir_instruction *first, - ir_instruction *last, - void *data) -{ - ir_instruction *ir, *ir_next; - /* List of avaialble_copy */ - exec_list assignments; - bool *out_progress = (bool *)data; - bool progress = false; - - void *ctx = ralloc_context(NULL); - /* Safe looping, since process_assignment */ - for (ir = first, ir_next = (ir_instruction *)first->next;; - ir = ir_next, ir_next = (ir_instruction *)ir->next) { - ir_assignment *ir_assign = ir->as_assignment(); - - if (debug) { - ir->print(); - printf("\n"); - } - - if (ir_assign) { - progress = process_assignment(ctx, ir_assign, &assignments) || progress; - } else { - kill_for_derefs_visitor kill(&assignments); - ir->accept(&kill); - } - - if (ir == last) - break; - } - *out_progress = progress; - ralloc_free(ctx); -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_dead_code_local(exec_list *instructions) -{ - bool progress = false; - - call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress); - - return progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_dead_code_local.cpp
+ *
+ * Eliminates local dead assignments from the code.
+ *
+ * This operates on basic blocks, tracking assignments and finding if
+ * they're used before the variable is completely reassigned.
+ *
+ * Compare this to ir_dead_code.cpp, which operates globally looking
+ * for assignments to variables that are never read.
+ */
+
+#include "ir.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+class assignment_entry : public exec_node
+{
+public:
+ assignment_entry(ir_variable *lhs, ir_instruction *ir)
+ {
+ assert(lhs);
+ assert(ir);
+ this->lhs = lhs;
+ this->ir = ir;
+ }
+
+ ir_variable *lhs;
+ ir_instruction *ir;
+};
+
+class kill_for_derefs_visitor : public ir_hierarchical_visitor {
+public:
+ kill_for_derefs_visitor(exec_list *assignments)
+ {
+ this->assignments = assignments;
+ }
+
+ virtual ir_visitor_status visit(ir_dereference_variable *ir)
+ {
+ ir_variable *const var = ir->variable_referenced();
+
+ foreach_iter(exec_list_iterator, iter, *this->assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ if (entry->lhs == var) {
+ if (debug)
+ printf("kill %s\n", entry->lhs->name);
+ entry->remove();
+ }
+ }
+
+ return visit_continue;
+ }
+
+private:
+ exec_list *assignments;
+};
+
+class array_index_visit : public ir_hierarchical_visitor {
+public:
+ array_index_visit(ir_hierarchical_visitor *v)
+ {
+ this->visitor = v;
+ }
+
+ virtual ir_visitor_status visit_enter(class ir_dereference_array *ir)
+ {
+ ir->array_index->accept(visitor);
+ return visit_continue;
+ }
+
+ static void run(ir_instruction *ir, ir_hierarchical_visitor *v)
+ {
+ array_index_visit top_visit(v);
+ ir->accept(& top_visit);
+ }
+
+ ir_hierarchical_visitor *visitor;
+};
+
+
+/**
+ * Adds an entry to the available copy list if it's a plain assignment
+ * of a variable to a variable.
+ */
+static bool
+process_assignment(void *ctx, ir_assignment *ir, exec_list *assignments)
+{
+ ir_variable *var = NULL;
+ bool progress = false;
+ kill_for_derefs_visitor v(assignments);
+
+ /* Kill assignment entries for things used to produce this assignment. */
+ ir->rhs->accept(&v);
+ if (ir->condition) {
+ ir->condition->accept(&v);
+ }
+
+ /* Kill assignment enties used as array indices.
+ */
+ array_index_visit::run(ir->lhs, &v);
+ var = ir->lhs->variable_referenced();
+ assert(var);
+
+ /* Now, check if we did a whole-variable assignment. */
+ if (!ir->condition && (ir->whole_variable_written() != NULL)) {
+ /* We did a whole-variable assignment. So, any instruction in
+ * the assignment list with the same LHS is dead.
+ */
+ if (debug)
+ printf("looking for %s to remove\n", var->name);
+ foreach_iter(exec_list_iterator, iter, *assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ if (entry->lhs == var) {
+ if (debug)
+ printf("removing %s\n", var->name);
+ entry->ir->remove();
+ entry->remove();
+ progress = true;
+ }
+ }
+ }
+
+ /* Add this instruction to the assignment list available to be removed.
+ * But not if the assignment has other side effects.
+ */
+ if (ir_has_call(ir))
+ return progress;
+
+ assignment_entry *entry = new(ctx) assignment_entry(var, ir);
+ assignments->push_tail(entry);
+
+ if (debug) {
+ printf("add %s\n", var->name);
+
+ printf("current entries\n");
+ foreach_iter(exec_list_iterator, iter, *assignments) {
+ assignment_entry *entry = (assignment_entry *)iter.get();
+
+ printf(" %s\n", entry->lhs->name);
+ }
+ }
+
+ return progress;
+}
+
+static void
+dead_code_local_basic_block(ir_instruction *first,
+ ir_instruction *last,
+ void *data)
+{
+ ir_instruction *ir, *ir_next;
+ /* List of avaialble_copy */
+ exec_list assignments;
+ bool *out_progress = (bool *)data;
+ bool progress = false;
+
+ void *ctx = ralloc_context(NULL);
+ /* Safe looping, since process_assignment */
+ for (ir = first, ir_next = (ir_instruction *)first->next;;
+ ir = ir_next, ir_next = (ir_instruction *)ir->next) {
+ ir_assignment *ir_assign = ir->as_assignment();
+
+ if (debug) {
+ ir->print();
+ printf("\n");
+ }
+
+ if (ir_assign) {
+ progress = process_assignment(ctx, ir_assign, &assignments) || progress;
+ } else {
+ kill_for_derefs_visitor kill(&assignments);
+ ir->accept(&kill);
+ }
+
+ if (ir == last)
+ break;
+ }
+ *out_progress = progress;
+ ralloc_free(ctx);
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_dead_code_local(exec_list *instructions)
+{
+ bool progress = false;
+
+ call_for_basic_blocks(instructions, dead_code_local_basic_block, &progress);
+
+ return progress;
+}
diff --git a/mesalib/src/glsl/opt_function_inlining.cpp b/mesalib/src/glsl/opt_function_inlining.cpp index 8fef358cc..3cc405b95 100644 --- a/mesalib/src/glsl/opt_function_inlining.cpp +++ b/mesalib/src/glsl/opt_function_inlining.cpp @@ -1,422 +1,422 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_function_inlining.cpp - * - * Replaces calls to functions with the body of the function. - */ - -#include <inttypes.h> -#include "ir.h" -#include "ir_visitor.h" -#include "ir_function_inlining.h" -#include "ir_expression_flattening.h" -#include "glsl_types.h" -#include "program/hash_table.h" - -static void -do_sampler_replacement(exec_list *instructions, - ir_variable *sampler, - ir_dereference *deref); - -class ir_function_inlining_visitor : public ir_hierarchical_visitor { -public: - ir_function_inlining_visitor() - { - progress = false; - } - - virtual ~ir_function_inlining_visitor() - { - /* empty */ - } - - virtual ir_visitor_status visit_enter(ir_expression *); - virtual ir_visitor_status visit_enter(ir_call *); - virtual ir_visitor_status visit_enter(ir_assignment *); - virtual ir_visitor_status visit_enter(ir_return *); - virtual ir_visitor_status visit_enter(ir_texture *); - virtual ir_visitor_status visit_enter(ir_swizzle *); - - bool progress; -}; - - -bool -automatic_inlining_predicate(ir_instruction *ir) -{ - ir_call *call = ir->as_call(); - - if (call && can_inline(call)) - return true; - - return false; -} - -bool -do_function_inlining(exec_list *instructions) -{ - ir_function_inlining_visitor v; - - do_expression_flattening(instructions, automatic_inlining_predicate); - - v.run(instructions); - - return v.progress; -} - -static void -replace_return_with_assignment(ir_instruction *ir, void *data) -{ - void *ctx = ralloc_parent(ir); - ir_variable *retval = (ir_variable *)data; - ir_return *ret = ir->as_return(); - - if (ret) { - if (ret->value) { - ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval); - ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL)); - } else { - /* un-valued return has to be the last return, or we shouldn't - * have reached here. (see can_inline()). - */ - assert(ret->next->is_tail_sentinel()); - ret->remove(); - } - } -} - -ir_rvalue * -ir_call::generate_inline(ir_instruction *next_ir) -{ - void *ctx = ralloc_parent(this); - ir_variable **parameters; - int num_parameters; - int i; - ir_variable *retval = NULL; - struct hash_table *ht; - - ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare); - - num_parameters = 0; - foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters) - num_parameters++; - - parameters = new ir_variable *[num_parameters]; - - /* Generate storage for the return value. */ - if (!this->callee->return_type->is_void()) { - retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val", - ir_var_auto); - next_ir->insert_before(retval); - } - - /* Generate the declarations for the parameters to our inlined code, - * and set up the mapping of real function body variables to ours. - */ - i = 0; - exec_list_iterator sig_param_iter = this->callee->parameters.iterator(); - exec_list_iterator param_iter = this->actual_parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); - ir_rvalue *param = (ir_rvalue *) param_iter.get(); - - /* Generate a new variable for the parameter. */ - if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { - /* For samplers, we want the inlined sampler references - * referencing the passed in sampler variable, since that - * will have the location information, which an assignment of - * a sampler wouldn't. Fix it up below. - */ - parameters[i] = NULL; - } else { - parameters[i] = sig_param->clone(ctx, ht); - parameters[i]->mode = ir_var_auto; - - /* Remove the read-only decoration becuase we're going to write - * directly to this variable. If the cloned variable is left - * read-only and the inlined function is inside a loop, the loop - * analysis code will get confused. - */ - parameters[i]->read_only = false; - next_ir->insert_before(parameters[i]); - } - - /* Move the actual param into our param variable if it's an 'in' type. */ - if (parameters[i] && (sig_param->mode == ir_var_in || - sig_param->mode == ir_var_const_in || - sig_param->mode == ir_var_inout)) { - ir_assignment *assign; - - assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]), - param, NULL); - next_ir->insert_before(assign); - } - - sig_param_iter.next(); - param_iter.next(); - } - - exec_list new_instructions; - - /* Generate the inlined body of the function to a new list */ - foreach_iter(exec_list_iterator, iter, callee->body) { - ir_instruction *ir = (ir_instruction *)iter.get(); - ir_instruction *new_ir = ir->clone(ctx, ht); - - new_instructions.push_tail(new_ir); - visit_tree(new_ir, replace_return_with_assignment, retval); - } - - /* If any samplers were passed in, replace any deref of the sampler - * with a deref of the sampler argument. - */ - param_iter = this->actual_parameters.iterator(); - sig_param_iter = this->callee->parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_instruction *const param = (ir_instruction *) param_iter.get(); - ir_variable *sig_param = (ir_variable *) sig_param_iter.get(); - - if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) { - ir_dereference *deref = param->as_dereference(); - - assert(deref); - do_sampler_replacement(&new_instructions, sig_param, deref); - } - param_iter.next(); - sig_param_iter.next(); - } - - /* Now push those new instructions in. */ - next_ir->insert_before(&new_instructions); - - /* Copy back the value of any 'out' parameters from the function body - * variables to our own. - */ - i = 0; - param_iter = this->actual_parameters.iterator(); - sig_param_iter = this->callee->parameters.iterator(); - for (i = 0; i < num_parameters; i++) { - ir_instruction *const param = (ir_instruction *) param_iter.get(); - const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get(); - - /* Move our param variable into the actual param if it's an 'out' type. */ - if (parameters[i] && (sig_param->mode == ir_var_out || - sig_param->mode == ir_var_inout)) { - ir_assignment *assign; - - assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(), - new(ctx) ir_dereference_variable(parameters[i]), - NULL); - next_ir->insert_before(assign); - } - - param_iter.next(); - sig_param_iter.next(); - } - - delete [] parameters; - - hash_table_dtor(ht); - - if (retval) - return new(ctx) ir_dereference_variable(retval); - else - return NULL; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_expression *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_return *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_texture *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_swizzle *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_call *ir) -{ - if (can_inline(ir)) { - /* If the call was part of some tree, then it should have been - * flattened out or we shouldn't have seen it because of a - * visit_continue_with_parent in this visitor. - */ - assert(ir == base_ir); - - (void) ir->generate_inline(ir); - ir->remove(); - this->progress = true; - } - - return visit_continue; -} - - -ir_visitor_status -ir_function_inlining_visitor::visit_enter(ir_assignment *ir) -{ - ir_call *call = ir->rhs->as_call(); - if (!call || !can_inline(call)) - return visit_continue; - - /* generates the parameter setup, function body, and returns the return - * value of the function - */ - ir_rvalue *rhs = call->generate_inline(ir); - assert(rhs); - - ir->rhs = rhs; - this->progress = true; - - return visit_continue; -} - -/** - * Replaces references to the "sampler" variable with a clone of "deref." - * - * From the spec, samplers can appear in the tree as function - * (non-out) parameters and as the result of array indexing and - * structure field selection. In our builtin implementation, they - * also appear in the sampler field of an ir_tex instruction. - */ - -class ir_sampler_replacement_visitor : public ir_hierarchical_visitor { -public: - ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref) - { - this->sampler = sampler; - this->deref = deref; - } - - virtual ~ir_sampler_replacement_visitor() - { - } - - virtual ir_visitor_status visit_leave(ir_call *); - virtual ir_visitor_status visit_leave(ir_dereference_array *); - virtual ir_visitor_status visit_leave(ir_dereference_record *); - virtual ir_visitor_status visit_leave(ir_texture *); - - void replace_deref(ir_dereference **deref); - void replace_rvalue(ir_rvalue **rvalue); - - ir_variable *sampler; - ir_dereference *deref; -}; - -void -ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref) -{ - ir_dereference_variable *deref_var = (*deref)->as_dereference_variable(); - if (deref_var && deref_var->var == this->sampler) { - *deref = this->deref->clone(ralloc_parent(*deref), NULL); - } -} - -void -ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue) -{ - if (!*rvalue) - return; - - ir_dereference *deref = (*rvalue)->as_dereference(); - - if (!deref) - return; - - replace_deref(&deref); - *rvalue = deref; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_texture *ir) -{ - replace_deref(&ir->sampler); - - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir) -{ - replace_rvalue(&ir->array); - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir) -{ - replace_rvalue(&ir->record); - return visit_continue; -} - -ir_visitor_status -ir_sampler_replacement_visitor::visit_leave(ir_call *ir) -{ - foreach_iter(exec_list_iterator, iter, *ir) { - ir_rvalue *param = (ir_rvalue *)iter.get(); - ir_rvalue *new_param = param; - replace_rvalue(&new_param); - - if (new_param != param) { - param->replace_with(new_param); - } - } - return visit_continue; -} - -static void -do_sampler_replacement(exec_list *instructions, - ir_variable *sampler, - ir_dereference *deref) -{ - ir_sampler_replacement_visitor v(sampler, deref); - - visit_list_elements(&v, instructions); -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_function_inlining.cpp
+ *
+ * Replaces calls to functions with the body of the function.
+ */
+
+#include <inttypes.h>
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_function_inlining.h"
+#include "ir_expression_flattening.h"
+#include "glsl_types.h"
+#include "program/hash_table.h"
+
+static void
+do_sampler_replacement(exec_list *instructions,
+ ir_variable *sampler,
+ ir_dereference *deref);
+
+class ir_function_inlining_visitor : public ir_hierarchical_visitor {
+public:
+ ir_function_inlining_visitor()
+ {
+ progress = false;
+ }
+
+ virtual ~ir_function_inlining_visitor()
+ {
+ /* empty */
+ }
+
+ virtual ir_visitor_status visit_enter(ir_expression *);
+ virtual ir_visitor_status visit_enter(ir_call *);
+ virtual ir_visitor_status visit_enter(ir_assignment *);
+ virtual ir_visitor_status visit_enter(ir_return *);
+ virtual ir_visitor_status visit_enter(ir_texture *);
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+
+ bool progress;
+};
+
+
+bool
+automatic_inlining_predicate(ir_instruction *ir)
+{
+ ir_call *call = ir->as_call();
+
+ if (call && can_inline(call))
+ return true;
+
+ return false;
+}
+
+bool
+do_function_inlining(exec_list *instructions)
+{
+ ir_function_inlining_visitor v;
+
+ do_expression_flattening(instructions, automatic_inlining_predicate);
+
+ v.run(instructions);
+
+ return v.progress;
+}
+
+static void
+replace_return_with_assignment(ir_instruction *ir, void *data)
+{
+ void *ctx = ralloc_parent(ir);
+ ir_variable *retval = (ir_variable *)data;
+ ir_return *ret = ir->as_return();
+
+ if (ret) {
+ if (ret->value) {
+ ir_rvalue *lhs = new(ctx) ir_dereference_variable(retval);
+ ret->replace_with(new(ctx) ir_assignment(lhs, ret->value, NULL));
+ } else {
+ /* un-valued return has to be the last return, or we shouldn't
+ * have reached here. (see can_inline()).
+ */
+ assert(ret->next->is_tail_sentinel());
+ ret->remove();
+ }
+ }
+}
+
+ir_rvalue *
+ir_call::generate_inline(ir_instruction *next_ir)
+{
+ void *ctx = ralloc_parent(this);
+ ir_variable **parameters;
+ int num_parameters;
+ int i;
+ ir_variable *retval = NULL;
+ struct hash_table *ht;
+
+ ht = hash_table_ctor(0, hash_table_pointer_hash, hash_table_pointer_compare);
+
+ num_parameters = 0;
+ foreach_iter(exec_list_iterator, iter_sig, this->callee->parameters)
+ num_parameters++;
+
+ parameters = new ir_variable *[num_parameters];
+
+ /* Generate storage for the return value. */
+ if (!this->callee->return_type->is_void()) {
+ retval = new(ctx) ir_variable(this->callee->return_type, "_ret_val",
+ ir_var_auto);
+ next_ir->insert_before(retval);
+ }
+
+ /* Generate the declarations for the parameters to our inlined code,
+ * and set up the mapping of real function body variables to ours.
+ */
+ i = 0;
+ exec_list_iterator sig_param_iter = this->callee->parameters.iterator();
+ exec_list_iterator param_iter = this->actual_parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
+ ir_rvalue *param = (ir_rvalue *) param_iter.get();
+
+ /* Generate a new variable for the parameter. */
+ if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
+ /* For samplers, we want the inlined sampler references
+ * referencing the passed in sampler variable, since that
+ * will have the location information, which an assignment of
+ * a sampler wouldn't. Fix it up below.
+ */
+ parameters[i] = NULL;
+ } else {
+ parameters[i] = sig_param->clone(ctx, ht);
+ parameters[i]->mode = ir_var_auto;
+
+ /* Remove the read-only decoration becuase we're going to write
+ * directly to this variable. If the cloned variable is left
+ * read-only and the inlined function is inside a loop, the loop
+ * analysis code will get confused.
+ */
+ parameters[i]->read_only = false;
+ next_ir->insert_before(parameters[i]);
+ }
+
+ /* Move the actual param into our param variable if it's an 'in' type. */
+ if (parameters[i] && (sig_param->mode == ir_var_in ||
+ sig_param->mode == ir_var_const_in ||
+ sig_param->mode == ir_var_inout)) {
+ ir_assignment *assign;
+
+ assign = new(ctx) ir_assignment(new(ctx) ir_dereference_variable(parameters[i]),
+ param, NULL);
+ next_ir->insert_before(assign);
+ }
+
+ sig_param_iter.next();
+ param_iter.next();
+ }
+
+ exec_list new_instructions;
+
+ /* Generate the inlined body of the function to a new list */
+ foreach_iter(exec_list_iterator, iter, callee->body) {
+ ir_instruction *ir = (ir_instruction *)iter.get();
+ ir_instruction *new_ir = ir->clone(ctx, ht);
+
+ new_instructions.push_tail(new_ir);
+ visit_tree(new_ir, replace_return_with_assignment, retval);
+ }
+
+ /* If any samplers were passed in, replace any deref of the sampler
+ * with a deref of the sampler argument.
+ */
+ param_iter = this->actual_parameters.iterator();
+ sig_param_iter = this->callee->parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_instruction *const param = (ir_instruction *) param_iter.get();
+ ir_variable *sig_param = (ir_variable *) sig_param_iter.get();
+
+ if (sig_param->type->base_type == GLSL_TYPE_SAMPLER) {
+ ir_dereference *deref = param->as_dereference();
+
+ assert(deref);
+ do_sampler_replacement(&new_instructions, sig_param, deref);
+ }
+ param_iter.next();
+ sig_param_iter.next();
+ }
+
+ /* Now push those new instructions in. */
+ next_ir->insert_before(&new_instructions);
+
+ /* Copy back the value of any 'out' parameters from the function body
+ * variables to our own.
+ */
+ i = 0;
+ param_iter = this->actual_parameters.iterator();
+ sig_param_iter = this->callee->parameters.iterator();
+ for (i = 0; i < num_parameters; i++) {
+ ir_instruction *const param = (ir_instruction *) param_iter.get();
+ const ir_variable *const sig_param = (ir_variable *) sig_param_iter.get();
+
+ /* Move our param variable into the actual param if it's an 'out' type. */
+ if (parameters[i] && (sig_param->mode == ir_var_out ||
+ sig_param->mode == ir_var_inout)) {
+ ir_assignment *assign;
+
+ assign = new(ctx) ir_assignment(param->clone(ctx, NULL)->as_rvalue(),
+ new(ctx) ir_dereference_variable(parameters[i]),
+ NULL);
+ next_ir->insert_before(assign);
+ }
+
+ param_iter.next();
+ sig_param_iter.next();
+ }
+
+ delete [] parameters;
+
+ hash_table_dtor(ht);
+
+ if (retval)
+ return new(ctx) ir_dereference_variable(retval);
+ else
+ return NULL;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_expression *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_return *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_texture *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_swizzle *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_call *ir)
+{
+ if (can_inline(ir)) {
+ /* If the call was part of some tree, then it should have been
+ * flattened out or we shouldn't have seen it because of a
+ * visit_continue_with_parent in this visitor.
+ */
+ assert(ir == base_ir);
+
+ (void) ir->generate_inline(ir);
+ ir->remove();
+ this->progress = true;
+ }
+
+ return visit_continue;
+}
+
+
+ir_visitor_status
+ir_function_inlining_visitor::visit_enter(ir_assignment *ir)
+{
+ ir_call *call = ir->rhs->as_call();
+ if (!call || !can_inline(call))
+ return visit_continue;
+
+ /* generates the parameter setup, function body, and returns the return
+ * value of the function
+ */
+ ir_rvalue *rhs = call->generate_inline(ir);
+ assert(rhs);
+
+ ir->rhs = rhs;
+ this->progress = true;
+
+ return visit_continue;
+}
+
+/**
+ * Replaces references to the "sampler" variable with a clone of "deref."
+ *
+ * From the spec, samplers can appear in the tree as function
+ * (non-out) parameters and as the result of array indexing and
+ * structure field selection. In our builtin implementation, they
+ * also appear in the sampler field of an ir_tex instruction.
+ */
+
+class ir_sampler_replacement_visitor : public ir_hierarchical_visitor {
+public:
+ ir_sampler_replacement_visitor(ir_variable *sampler, ir_dereference *deref)
+ {
+ this->sampler = sampler;
+ this->deref = deref;
+ }
+
+ virtual ~ir_sampler_replacement_visitor()
+ {
+ }
+
+ virtual ir_visitor_status visit_leave(ir_call *);
+ virtual ir_visitor_status visit_leave(ir_dereference_array *);
+ virtual ir_visitor_status visit_leave(ir_dereference_record *);
+ virtual ir_visitor_status visit_leave(ir_texture *);
+
+ void replace_deref(ir_dereference **deref);
+ void replace_rvalue(ir_rvalue **rvalue);
+
+ ir_variable *sampler;
+ ir_dereference *deref;
+};
+
+void
+ir_sampler_replacement_visitor::replace_deref(ir_dereference **deref)
+{
+ ir_dereference_variable *deref_var = (*deref)->as_dereference_variable();
+ if (deref_var && deref_var->var == this->sampler) {
+ *deref = this->deref->clone(ralloc_parent(*deref), NULL);
+ }
+}
+
+void
+ir_sampler_replacement_visitor::replace_rvalue(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return;
+
+ ir_dereference *deref = (*rvalue)->as_dereference();
+
+ if (!deref)
+ return;
+
+ replace_deref(&deref);
+ *rvalue = deref;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_texture *ir)
+{
+ replace_deref(&ir->sampler);
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_dereference_array *ir)
+{
+ replace_rvalue(&ir->array);
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_dereference_record *ir)
+{
+ replace_rvalue(&ir->record);
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_sampler_replacement_visitor::visit_leave(ir_call *ir)
+{
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_rvalue *param = (ir_rvalue *)iter.get();
+ ir_rvalue *new_param = param;
+ replace_rvalue(&new_param);
+
+ if (new_param != param) {
+ param->replace_with(new_param);
+ }
+ }
+ return visit_continue;
+}
+
+static void
+do_sampler_replacement(exec_list *instructions,
+ ir_variable *sampler,
+ ir_dereference *deref)
+{
+ ir_sampler_replacement_visitor v(sampler, deref);
+
+ visit_list_elements(&v, instructions);
+}
diff --git a/mesalib/src/glsl/opt_swizzle_swizzle.cpp b/mesalib/src/glsl/opt_swizzle_swizzle.cpp index bc442fa86..e23655240 100644 --- a/mesalib/src/glsl/opt_swizzle_swizzle.cpp +++ b/mesalib/src/glsl/opt_swizzle_swizzle.cpp @@ -1,93 +1,93 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_swizzle_swizzle.cpp - * - * Eliminates the second swizzle in a swizzle chain. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor { -public: - ir_swizzle_swizzle_visitor() - { - progress = false; - } - - virtual ir_visitor_status visit_enter(ir_swizzle *); - - bool progress; -}; - -ir_visitor_status -ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir) -{ - int mask2[4]; - - ir_swizzle *swiz2 = ir->val->as_swizzle(); - if (!swiz2) - return visit_continue; - - memset(&mask2, 0, sizeof(mask2)); - if (swiz2->mask.num_components >= 1) - mask2[0] = swiz2->mask.x; - if (swiz2->mask.num_components >= 2) - mask2[1] = swiz2->mask.y; - if (swiz2->mask.num_components >= 3) - mask2[2] = swiz2->mask.z; - if (swiz2->mask.num_components >= 4) - mask2[3] = swiz2->mask.w; - - if (ir->mask.num_components >= 1) - ir->mask.x = mask2[ir->mask.x]; - if (ir->mask.num_components >= 2) - ir->mask.y = mask2[ir->mask.y]; - if (ir->mask.num_components >= 3) - ir->mask.z = mask2[ir->mask.z]; - if (ir->mask.num_components >= 4) - ir->mask.w = mask2[ir->mask.w]; - - ir->val = swiz2->val; - - this->progress = true; - - return visit_continue; -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_swizzle_swizzle(exec_list *instructions) -{ - ir_swizzle_swizzle_visitor v; - - v.run(instructions); - - return v.progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_swizzle_swizzle.cpp
+ *
+ * Eliminates the second swizzle in a swizzle chain.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+class ir_swizzle_swizzle_visitor : public ir_hierarchical_visitor {
+public:
+ ir_swizzle_swizzle_visitor()
+ {
+ progress = false;
+ }
+
+ virtual ir_visitor_status visit_enter(ir_swizzle *);
+
+ bool progress;
+};
+
+ir_visitor_status
+ir_swizzle_swizzle_visitor::visit_enter(ir_swizzle *ir)
+{
+ int mask2[4];
+
+ ir_swizzle *swiz2 = ir->val->as_swizzle();
+ if (!swiz2)
+ return visit_continue;
+
+ memset(&mask2, 0, sizeof(mask2));
+ if (swiz2->mask.num_components >= 1)
+ mask2[0] = swiz2->mask.x;
+ if (swiz2->mask.num_components >= 2)
+ mask2[1] = swiz2->mask.y;
+ if (swiz2->mask.num_components >= 3)
+ mask2[2] = swiz2->mask.z;
+ if (swiz2->mask.num_components >= 4)
+ mask2[3] = swiz2->mask.w;
+
+ if (ir->mask.num_components >= 1)
+ ir->mask.x = mask2[ir->mask.x];
+ if (ir->mask.num_components >= 2)
+ ir->mask.y = mask2[ir->mask.y];
+ if (ir->mask.num_components >= 3)
+ ir->mask.z = mask2[ir->mask.z];
+ if (ir->mask.num_components >= 4)
+ ir->mask.w = mask2[ir->mask.w];
+
+ ir->val = swiz2->val;
+
+ this->progress = true;
+
+ return visit_continue;
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_swizzle_swizzle(exec_list *instructions)
+{
+ ir_swizzle_swizzle_visitor v;
+
+ v.run(instructions);
+
+ return v.progress;
+}
diff --git a/mesalib/src/glsl/opt_tree_grafting.cpp b/mesalib/src/glsl/opt_tree_grafting.cpp index 1ef940f9c..75bd11c09 100644 --- a/mesalib/src/glsl/opt_tree_grafting.cpp +++ b/mesalib/src/glsl/opt_tree_grafting.cpp @@ -1,368 +1,368 @@ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -/** - * \file opt_tree_grafting.cpp - * - * Takes assignments to variables that are dereferenced only once and - * pastes the RHS expression into where the variable is dereferenced. - * - * In the process of various operations like function inlining and - * tertiary op handling, we'll end up with our expression trees having - * been chopped up into a series of assignments of short expressions - * to temps. Other passes like ir_algebraic.cpp would prefer to see - * the deepest expression trees they can to try to optimize them. - * - * This is a lot like copy propagaton. In comparison, copy - * propagation only acts on plain copies, not arbitrary expressions on - * the RHS. Generally, we wouldn't want to go pasting some - * complicated expression everywhere it got used, though, so we don't - * handle expressions in that pass. - * - * The hard part is making sure we don't move an expression across - * some other assignments that would change the value of the - * expression. So we split this into two passes: First, find the - * variables in our scope which are written to once and read once, and - * then go through basic blocks seeing if we find an opportunity to - * move those expressions safely. - */ - -#include "ir.h" -#include "ir_visitor.h" -#include "ir_variable_refcount.h" -#include "ir_basic_block.h" -#include "ir_optimization.h" -#include "glsl_types.h" - -static bool debug = false; - -class ir_tree_grafting_visitor : public ir_hierarchical_visitor { -public: - ir_tree_grafting_visitor(ir_assignment *graft_assign, - ir_variable *graft_var) - { - this->progress = false; - this->graft_assign = graft_assign; - this->graft_var = graft_var; - } - - virtual ir_visitor_status visit_leave(class ir_assignment *); - virtual ir_visitor_status visit_enter(class ir_call *); - virtual ir_visitor_status visit_enter(class ir_expression *); - virtual ir_visitor_status visit_enter(class ir_function *); - virtual ir_visitor_status visit_enter(class ir_function_signature *); - virtual ir_visitor_status visit_enter(class ir_if *); - virtual ir_visitor_status visit_enter(class ir_loop *); - virtual ir_visitor_status visit_enter(class ir_swizzle *); - virtual ir_visitor_status visit_enter(class ir_texture *); - - bool do_graft(ir_rvalue **rvalue); - - bool progress; - ir_variable *graft_var; - ir_assignment *graft_assign; -}; - -struct find_deref_info { - ir_variable *var; - bool found; -}; - -void -dereferences_variable_callback(ir_instruction *ir, void *data) -{ - struct find_deref_info *info = (struct find_deref_info *)data; - ir_dereference_variable *deref = ir->as_dereference_variable(); - - if (deref && deref->var == info->var) - info->found = true; -} - -static bool -dereferences_variable(ir_instruction *ir, ir_variable *var) -{ - struct find_deref_info info; - - info.var = var; - info.found = false; - - visit_tree(ir, dereferences_variable_callback, &info); - - return info.found; -} - -bool -ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue) -{ - if (!*rvalue) - return false; - - ir_dereference_variable *deref = (*rvalue)->as_dereference_variable(); - - if (!deref || deref->var != this->graft_var) - return false; - - if (debug) { - printf("GRAFTING:\n"); - this->graft_assign->print(); - printf("\n"); - printf("TO:\n"); - (*rvalue)->print(); - printf("\n"); - } - - this->graft_assign->remove(); - *rvalue = this->graft_assign->rhs; - - this->progress = true; - return true; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_loop *ir) -{ - (void)ir; - /* Do not traverse into the body of the loop since that is a - * different basic block. - */ - return visit_stop; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_leave(ir_assignment *ir) -{ - if (do_graft(&ir->rhs) || - do_graft(&ir->condition)) - return visit_stop; - - /* If this assignment updates a variable used in the assignment - * we're trying to graft, then we're done. - */ - if (dereferences_variable(this->graft_assign->rhs, - ir->lhs->variable_referenced())) { - if (debug) { - printf("graft killed by: "); - ir->print(); - printf("\n"); - } - return visit_stop; - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_function *ir) -{ - (void) ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir) -{ - (void)ir; - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_call *ir) -{ - exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator(); - /* Reminder: iterating ir_call iterates its parameters. */ - foreach_iter(exec_list_iterator, iter, *ir) { - ir_variable *sig_param = (ir_variable *)sig_iter.get(); - ir_rvalue *ir = (ir_rvalue *)iter.get(); - ir_rvalue *new_ir = ir; - - if (sig_param->mode != ir_var_in && sig_param->mode != ir_var_const_in) - continue; - - if (do_graft(&new_ir)) { - ir->replace_with(new_ir); - return visit_stop; - } - sig_iter.next(); - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_expression *ir) -{ - for (unsigned int i = 0; i < ir->get_num_operands(); i++) { - if (do_graft(&ir->operands[i])) - return visit_stop; - } - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_if *ir) -{ - if (do_graft(&ir->condition)) - return visit_stop; - - /* Do not traverse into the body of the if-statement since that is a - * different basic block. - */ - return visit_continue_with_parent; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir) -{ - if (do_graft(&ir->val)) - return visit_stop; - - return visit_continue; -} - -ir_visitor_status -ir_tree_grafting_visitor::visit_enter(ir_texture *ir) -{ - if (do_graft(&ir->coordinate) || - do_graft(&ir->projector) || - do_graft(&ir->offset) || - do_graft(&ir->shadow_comparitor)) - return visit_stop; - - switch (ir->op) { - case ir_tex: - break; - case ir_txb: - if (do_graft(&ir->lod_info.bias)) - return visit_stop; - break; - case ir_txf: - case ir_txl: - if (do_graft(&ir->lod_info.lod)) - return visit_stop; - break; - case ir_txd: - if (do_graft(&ir->lod_info.grad.dPdx) || - do_graft(&ir->lod_info.grad.dPdy)) - return visit_stop; - break; - } - - return visit_continue; -} - -struct tree_grafting_info { - ir_variable_refcount_visitor *refs; - bool progress; -}; - -static bool -try_tree_grafting(ir_assignment *start, - ir_variable *lhs_var, - ir_instruction *bb_last) -{ - ir_tree_grafting_visitor v(start, lhs_var); - - if (debug) { - printf("trying to graft: "); - lhs_var->print(); - printf("\n"); - } - - for (ir_instruction *ir = (ir_instruction *)start->next; - ir != bb_last->next; - ir = (ir_instruction *)ir->next) { - - if (debug) { - printf("- "); - ir->print(); - printf("\n"); - } - - ir_visitor_status s = ir->accept(&v); - if (s == visit_stop) - return v.progress; - } - - return false; -} - -static void -tree_grafting_basic_block(ir_instruction *bb_first, - ir_instruction *bb_last, - void *data) -{ - struct tree_grafting_info *info = (struct tree_grafting_info *)data; - ir_instruction *ir, *next; - - for (ir = bb_first, next = (ir_instruction *)ir->next; - ir != bb_last->next; - ir = next, next = (ir_instruction *)ir->next) { - ir_assignment *assign = ir->as_assignment(); - - if (!assign) - continue; - - ir_variable *lhs_var = assign->whole_variable_written(); - if (!lhs_var) - continue; - - if (lhs_var->mode == ir_var_out || - lhs_var->mode == ir_var_inout) - continue; - - variable_entry *entry = info->refs->get_variable_entry(lhs_var); - - if (!entry->declaration || - entry->assigned_count != 1 || - entry->referenced_count != 2) - continue; - - assert(assign == entry->assign); - - /* Found a possibly graftable assignment. Now, walk through the - * rest of the BB seeing if the deref is here, and if nothing interfered with - * pasting its expression's values in between. - */ - info->progress |= try_tree_grafting(assign, lhs_var, bb_last); - } -} - -/** - * Does a copy propagation pass on the code present in the instruction stream. - */ -bool -do_tree_grafting(exec_list *instructions) -{ - ir_variable_refcount_visitor refs; - struct tree_grafting_info info; - - info.progress = false; - info.refs = &refs; - - visit_list_elements(info.refs, instructions); - - call_for_basic_blocks(instructions, tree_grafting_basic_block, &info); - - return info.progress; -} +/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_tree_grafting.cpp
+ *
+ * Takes assignments to variables that are dereferenced only once and
+ * pastes the RHS expression into where the variable is dereferenced.
+ *
+ * In the process of various operations like function inlining and
+ * tertiary op handling, we'll end up with our expression trees having
+ * been chopped up into a series of assignments of short expressions
+ * to temps. Other passes like ir_algebraic.cpp would prefer to see
+ * the deepest expression trees they can to try to optimize them.
+ *
+ * This is a lot like copy propagaton. In comparison, copy
+ * propagation only acts on plain copies, not arbitrary expressions on
+ * the RHS. Generally, we wouldn't want to go pasting some
+ * complicated expression everywhere it got used, though, so we don't
+ * handle expressions in that pass.
+ *
+ * The hard part is making sure we don't move an expression across
+ * some other assignments that would change the value of the
+ * expression. So we split this into two passes: First, find the
+ * variables in our scope which are written to once and read once, and
+ * then go through basic blocks seeing if we find an opportunity to
+ * move those expressions safely.
+ */
+
+#include "ir.h"
+#include "ir_visitor.h"
+#include "ir_variable_refcount.h"
+#include "ir_basic_block.h"
+#include "ir_optimization.h"
+#include "glsl_types.h"
+
+static bool debug = false;
+
+class ir_tree_grafting_visitor : public ir_hierarchical_visitor {
+public:
+ ir_tree_grafting_visitor(ir_assignment *graft_assign,
+ ir_variable *graft_var)
+ {
+ this->progress = false;
+ this->graft_assign = graft_assign;
+ this->graft_var = graft_var;
+ }
+
+ virtual ir_visitor_status visit_leave(class ir_assignment *);
+ virtual ir_visitor_status visit_enter(class ir_call *);
+ virtual ir_visitor_status visit_enter(class ir_expression *);
+ virtual ir_visitor_status visit_enter(class ir_function *);
+ virtual ir_visitor_status visit_enter(class ir_function_signature *);
+ virtual ir_visitor_status visit_enter(class ir_if *);
+ virtual ir_visitor_status visit_enter(class ir_loop *);
+ virtual ir_visitor_status visit_enter(class ir_swizzle *);
+ virtual ir_visitor_status visit_enter(class ir_texture *);
+
+ bool do_graft(ir_rvalue **rvalue);
+
+ bool progress;
+ ir_variable *graft_var;
+ ir_assignment *graft_assign;
+};
+
+struct find_deref_info {
+ ir_variable *var;
+ bool found;
+};
+
+void
+dereferences_variable_callback(ir_instruction *ir, void *data)
+{
+ struct find_deref_info *info = (struct find_deref_info *)data;
+ ir_dereference_variable *deref = ir->as_dereference_variable();
+
+ if (deref && deref->var == info->var)
+ info->found = true;
+}
+
+static bool
+dereferences_variable(ir_instruction *ir, ir_variable *var)
+{
+ struct find_deref_info info;
+
+ info.var = var;
+ info.found = false;
+
+ visit_tree(ir, dereferences_variable_callback, &info);
+
+ return info.found;
+}
+
+bool
+ir_tree_grafting_visitor::do_graft(ir_rvalue **rvalue)
+{
+ if (!*rvalue)
+ return false;
+
+ ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
+
+ if (!deref || deref->var != this->graft_var)
+ return false;
+
+ if (debug) {
+ printf("GRAFTING:\n");
+ this->graft_assign->print();
+ printf("\n");
+ printf("TO:\n");
+ (*rvalue)->print();
+ printf("\n");
+ }
+
+ this->graft_assign->remove();
+ *rvalue = this->graft_assign->rhs;
+
+ this->progress = true;
+ return true;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_loop *ir)
+{
+ (void)ir;
+ /* Do not traverse into the body of the loop since that is a
+ * different basic block.
+ */
+ return visit_stop;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_leave(ir_assignment *ir)
+{
+ if (do_graft(&ir->rhs) ||
+ do_graft(&ir->condition))
+ return visit_stop;
+
+ /* If this assignment updates a variable used in the assignment
+ * we're trying to graft, then we're done.
+ */
+ if (dereferences_variable(this->graft_assign->rhs,
+ ir->lhs->variable_referenced())) {
+ if (debug) {
+ printf("graft killed by: ");
+ ir->print();
+ printf("\n");
+ }
+ return visit_stop;
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_function *ir)
+{
+ (void) ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_function_signature *ir)
+{
+ (void)ir;
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_call *ir)
+{
+ exec_list_iterator sig_iter = ir->get_callee()->parameters.iterator();
+ /* Reminder: iterating ir_call iterates its parameters. */
+ foreach_iter(exec_list_iterator, iter, *ir) {
+ ir_variable *sig_param = (ir_variable *)sig_iter.get();
+ ir_rvalue *ir = (ir_rvalue *)iter.get();
+ ir_rvalue *new_ir = ir;
+
+ if (sig_param->mode != ir_var_in && sig_param->mode != ir_var_const_in)
+ continue;
+
+ if (do_graft(&new_ir)) {
+ ir->replace_with(new_ir);
+ return visit_stop;
+ }
+ sig_iter.next();
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_expression *ir)
+{
+ for (unsigned int i = 0; i < ir->get_num_operands(); i++) {
+ if (do_graft(&ir->operands[i]))
+ return visit_stop;
+ }
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_if *ir)
+{
+ if (do_graft(&ir->condition))
+ return visit_stop;
+
+ /* Do not traverse into the body of the if-statement since that is a
+ * different basic block.
+ */
+ return visit_continue_with_parent;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_swizzle *ir)
+{
+ if (do_graft(&ir->val))
+ return visit_stop;
+
+ return visit_continue;
+}
+
+ir_visitor_status
+ir_tree_grafting_visitor::visit_enter(ir_texture *ir)
+{
+ if (do_graft(&ir->coordinate) ||
+ do_graft(&ir->projector) ||
+ do_graft(&ir->offset) ||
+ do_graft(&ir->shadow_comparitor))
+ return visit_stop;
+
+ switch (ir->op) {
+ case ir_tex:
+ break;
+ case ir_txb:
+ if (do_graft(&ir->lod_info.bias))
+ return visit_stop;
+ break;
+ case ir_txf:
+ case ir_txl:
+ if (do_graft(&ir->lod_info.lod))
+ return visit_stop;
+ break;
+ case ir_txd:
+ if (do_graft(&ir->lod_info.grad.dPdx) ||
+ do_graft(&ir->lod_info.grad.dPdy))
+ return visit_stop;
+ break;
+ }
+
+ return visit_continue;
+}
+
+struct tree_grafting_info {
+ ir_variable_refcount_visitor *refs;
+ bool progress;
+};
+
+static bool
+try_tree_grafting(ir_assignment *start,
+ ir_variable *lhs_var,
+ ir_instruction *bb_last)
+{
+ ir_tree_grafting_visitor v(start, lhs_var);
+
+ if (debug) {
+ printf("trying to graft: ");
+ lhs_var->print();
+ printf("\n");
+ }
+
+ for (ir_instruction *ir = (ir_instruction *)start->next;
+ ir != bb_last->next;
+ ir = (ir_instruction *)ir->next) {
+
+ if (debug) {
+ printf("- ");
+ ir->print();
+ printf("\n");
+ }
+
+ ir_visitor_status s = ir->accept(&v);
+ if (s == visit_stop)
+ return v.progress;
+ }
+
+ return false;
+}
+
+static void
+tree_grafting_basic_block(ir_instruction *bb_first,
+ ir_instruction *bb_last,
+ void *data)
+{
+ struct tree_grafting_info *info = (struct tree_grafting_info *)data;
+ ir_instruction *ir, *next;
+
+ for (ir = bb_first, next = (ir_instruction *)ir->next;
+ ir != bb_last->next;
+ ir = next, next = (ir_instruction *)ir->next) {
+ ir_assignment *assign = ir->as_assignment();
+
+ if (!assign)
+ continue;
+
+ ir_variable *lhs_var = assign->whole_variable_written();
+ if (!lhs_var)
+ continue;
+
+ if (lhs_var->mode == ir_var_out ||
+ lhs_var->mode == ir_var_inout)
+ continue;
+
+ variable_entry *entry = info->refs->get_variable_entry(lhs_var);
+
+ if (!entry->declaration ||
+ entry->assigned_count != 1 ||
+ entry->referenced_count != 2)
+ continue;
+
+ assert(assign == entry->assign);
+
+ /* Found a possibly graftable assignment. Now, walk through the
+ * rest of the BB seeing if the deref is here, and if nothing interfered with
+ * pasting its expression's values in between.
+ */
+ info->progress |= try_tree_grafting(assign, lhs_var, bb_last);
+ }
+}
+
+/**
+ * Does a copy propagation pass on the code present in the instruction stream.
+ */
+bool
+do_tree_grafting(exec_list *instructions)
+{
+ ir_variable_refcount_visitor refs;
+ struct tree_grafting_info info;
+
+ info.progress = false;
+ info.refs = &refs;
+
+ visit_list_elements(info.refs, instructions);
+
+ call_for_basic_blocks(instructions, tree_grafting_basic_block, &info);
+
+ return info.progress;
+}
diff --git a/mesalib/src/glsl/s_expression.cpp b/mesalib/src/glsl/s_expression.cpp index a922a50d3..b47b014c6 100644 --- a/mesalib/src/glsl/s_expression.cpp +++ b/mesalib/src/glsl/s_expression.cpp @@ -1,183 +1,183 @@ -/* -*- c++ -*- */ -/* - * Copyright © 2010 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include <assert.h> -#include "s_expression.h" - -s_symbol::s_symbol(const char *tmp, size_t n) -{ - this->str = ralloc_strndup (this, tmp, n); - assert(this->str != NULL); -} - -s_list::s_list() -{ -} - -static void -skip_whitespace(const char *& src) -{ - src += strspn(src, " \v\t\r\n"); - /* Also skip Scheme-style comments: semi-colon 'til end of line */ - if (src[0] == ';') { - src += strcspn(src, "\n"); - skip_whitespace(src); - } -} - -static s_expression * -read_atom(void *ctx, const char *& src) -{ - s_expression *expr = NULL; - - skip_whitespace(src); - - size_t n = strcspn(src, "( \v\t\r\n);"); - if (n == 0) - return NULL; // no atom - - // Check if the atom is a number. - char *float_end = NULL; - double f = glsl_strtod(src, &float_end); - if (float_end != src) { - char *int_end = NULL; - int i = strtol(src, &int_end, 10); - // If strtod matched more characters, it must have a decimal part - if (float_end > int_end) - expr = new(ctx) s_float(f); - else - expr = new(ctx) s_int(i); - } else { - // Not a number; return a symbol. - expr = new(ctx) s_symbol(src, n); - } - - src += n; - - return expr; -} - -s_expression * -s_expression::read_expression(void *ctx, const char *&src) -{ - assert(src != NULL); - - s_expression *atom = read_atom(ctx, src); - if (atom != NULL) - return atom; - - skip_whitespace(src); - if (src[0] == '(') { - ++src; - - s_list *list = new(ctx) s_list; - s_expression *expr; - - while ((expr = read_expression(ctx, src)) != NULL) { - list->subexpressions.push_tail(expr); - } - skip_whitespace(src); - if (src[0] != ')') { - printf("Unclosed expression (check your parenthesis).\n"); - return NULL; - } - ++src; - return list; - } - return NULL; -} - -void s_int::print() -{ - printf("%d", this->val); -} - -void s_float::print() -{ - printf("%f", this->val); -} - -void s_symbol::print() -{ - printf("%s", this->str); -} - -void s_list::print() -{ - printf("("); - foreach_iter(exec_list_iterator, it, this->subexpressions) { - s_expression *expr = (s_expression*) it.get(); - expr->print(); - if (!expr->next->is_tail_sentinel()) - printf(" "); - } - printf(")"); -} - -// -------------------------------------------------- - -bool -s_pattern::match(s_expression *expr) -{ - switch (type) - { - case EXPR: *p_expr = expr; break; - case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break; - case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break; - case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break; - case INT: if (expr->is_int()) *p_int = (s_int *) expr; break; - case STRING: - s_symbol *sym = SX_AS_SYMBOL(expr); - if (sym != NULL && strcmp(sym->value(), literal) == 0) - return true; - return false; - }; - - return *p_expr == expr; -} - -bool -s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial) -{ - s_list *list = SX_AS_LIST(top); - if (list == NULL) - return false; - - unsigned i = 0; - foreach_iter(exec_list_iterator, it, list->subexpressions) { - if (i >= n) - return partial; /* More actual items than the pattern expected */ - - s_expression *expr = (s_expression *) it.get(); - if (expr == NULL || !pattern[i].match(expr)) - return false; - - i++; - } - - if (i < n) - return false; /* Less actual items than the pattern expected */ - - return true; -} +/* -*- c++ -*- */
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include "s_expression.h"
+
+s_symbol::s_symbol(const char *tmp, size_t n)
+{
+ this->str = ralloc_strndup (this, tmp, n);
+ assert(this->str != NULL);
+}
+
+s_list::s_list()
+{
+}
+
+static void
+skip_whitespace(const char *& src)
+{
+ src += strspn(src, " \v\t\r\n");
+ /* Also skip Scheme-style comments: semi-colon 'til end of line */
+ if (src[0] == ';') {
+ src += strcspn(src, "\n");
+ skip_whitespace(src);
+ }
+}
+
+static s_expression *
+read_atom(void *ctx, const char *& src)
+{
+ s_expression *expr = NULL;
+
+ skip_whitespace(src);
+
+ size_t n = strcspn(src, "( \v\t\r\n);");
+ if (n == 0)
+ return NULL; // no atom
+
+ // Check if the atom is a number.
+ char *float_end = NULL;
+ double f = glsl_strtod(src, &float_end);
+ if (float_end != src) {
+ char *int_end = NULL;
+ int i = strtol(src, &int_end, 10);
+ // If strtod matched more characters, it must have a decimal part
+ if (float_end > int_end)
+ expr = new(ctx) s_float(f);
+ else
+ expr = new(ctx) s_int(i);
+ } else {
+ // Not a number; return a symbol.
+ expr = new(ctx) s_symbol(src, n);
+ }
+
+ src += n;
+
+ return expr;
+}
+
+s_expression *
+s_expression::read_expression(void *ctx, const char *&src)
+{
+ assert(src != NULL);
+
+ s_expression *atom = read_atom(ctx, src);
+ if (atom != NULL)
+ return atom;
+
+ skip_whitespace(src);
+ if (src[0] == '(') {
+ ++src;
+
+ s_list *list = new(ctx) s_list;
+ s_expression *expr;
+
+ while ((expr = read_expression(ctx, src)) != NULL) {
+ list->subexpressions.push_tail(expr);
+ }
+ skip_whitespace(src);
+ if (src[0] != ')') {
+ printf("Unclosed expression (check your parenthesis).\n");
+ return NULL;
+ }
+ ++src;
+ return list;
+ }
+ return NULL;
+}
+
+void s_int::print()
+{
+ printf("%d", this->val);
+}
+
+void s_float::print()
+{
+ printf("%f", this->val);
+}
+
+void s_symbol::print()
+{
+ printf("%s", this->str);
+}
+
+void s_list::print()
+{
+ printf("(");
+ foreach_iter(exec_list_iterator, it, this->subexpressions) {
+ s_expression *expr = (s_expression*) it.get();
+ expr->print();
+ if (!expr->next->is_tail_sentinel())
+ printf(" ");
+ }
+ printf(")");
+}
+
+// --------------------------------------------------
+
+bool
+s_pattern::match(s_expression *expr)
+{
+ switch (type)
+ {
+ case EXPR: *p_expr = expr; break;
+ case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break;
+ case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break;
+ case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break;
+ case INT: if (expr->is_int()) *p_int = (s_int *) expr; break;
+ case STRING:
+ s_symbol *sym = SX_AS_SYMBOL(expr);
+ if (sym != NULL && strcmp(sym->value(), literal) == 0)
+ return true;
+ return false;
+ };
+
+ return *p_expr == expr;
+}
+
+bool
+s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial)
+{
+ s_list *list = SX_AS_LIST(top);
+ if (list == NULL)
+ return false;
+
+ unsigned i = 0;
+ foreach_iter(exec_list_iterator, it, list->subexpressions) {
+ if (i >= n)
+ return partial; /* More actual items than the pattern expected */
+
+ s_expression *expr = (s_expression *) it.get();
+ if (expr == NULL || !pattern[i].match(expr))
+ return false;
+
+ i++;
+ }
+
+ if (i < n)
+ return false; /* Less actual items than the pattern expected */
+
+ return true;
+}
|