diff options
author | Stéphane Marchesin <[email protected]> | 2011-08-26 17:37:25 -0700 |
---|---|---|
committer | Stéphane Marchesin <[email protected]> | 2011-08-26 17:37:25 -0700 |
commit | f8e6d19f3f40931be741b44d3edf210c38e13f0f (patch) | |
tree | e99e4c619901412ac6448534b0f57ce1c4295c6b /src/glsl | |
parent | 974c49ed176de55aadb335a2956ef5dfec774a23 (diff) | |
parent | e3b0e3776646d0367206e4544229622eb22fe9f8 (diff) |
Merge branch 'master' of git://anongit.freedesktop.org/mesa/mesa
Diffstat (limited to 'src/glsl')
104 files changed, 3798 insertions, 611 deletions
diff --git a/src/glsl/.gitignore b/src/glsl/.gitignore index dfbd572d894..d26839a3e3e 100644 --- a/src/glsl/.gitignore +++ b/src/glsl/.gitignore @@ -5,3 +5,4 @@ glsl_parser.h glsl_parser.output builtin_function.cpp builtin_compiler +glsl_test diff --git a/src/glsl/Android.gen.mk b/src/glsl/Android.gen.mk new file mode 100644 index 00000000000..e4ccb7291ef --- /dev/null +++ b/src/glsl/Android.gen.mk @@ -0,0 +1,98 @@ +# Mesa 3-D graphics library +# +# Copyright (C) 2010-2011 Chia-I Wu <[email protected]> +# Copyright (C) 2010-2011 LunarG Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# 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. + +# included by glsl Android.mk for source generation + +ifeq ($(LOCAL_MODULE_CLASS),) +LOCAL_MODULE_CLASS := STATIC_LIBRARIES +endif + +intermediates := $(call local-intermediates-dir) + +sources := \ + glsl_lexer.cpp \ + glsl_parser.cpp \ + glcpp/glcpp-lex.c \ + glcpp/glcpp-parse.c + +ifneq ($(LOCAL_IS_HOST_MODULE),true) +sources += builtin_function.cpp +endif + +LOCAL_SRC_FILES := $(filter-out $(sources), $(LOCAL_SRC_FILES)) + +LOCAL_C_INCLUDES += $(intermediates) $(intermediates)/glcpp $(MESA_TOP)/src/glsl/glcpp + +sources := $(addprefix $(intermediates)/, $(sources)) +LOCAL_GENERATED_SOURCES += $(sources) + +define local-l-or-ll-to-c-or-cpp + @mkdir -p $(dir $@) + @echo "Mesa Lex: $(PRIVATE_MODULE) <= $<" + $(hide) $(LEX) --nounistd -o$@ $< +endef + +define local-y-to-c-and-h + @mkdir -p $(dir $@) + @echo "Mesa Yacc: $(PRIVATE_MODULE) <= $<" + $(hide) $(YACC) -o $@ $< +endef + +define local-yy-to-cpp-and-h + @mkdir -p $(dir $@) + @echo "Mesa Yacc: $(PRIVATE_MODULE) <= $<" + $(hide) $(YACC) -p "_mesa_glsl_" -o $@ $< + touch $(@:$1=$(YACC_HEADER_SUFFIX)) + echo '#ifndef '$(@F:$1=_h) > $(@:$1=.h) + echo '#define '$(@F:$1=_h) >> $(@:$1=.h) + cat $(@:$1=$(YACC_HEADER_SUFFIX)) >> $(@:$1=.h) + echo '#endif' >> $(@:$1=.h) + rm -f $(@:$1=$(YACC_HEADER_SUFFIX)) +endef + +$(intermediates)/glsl_lexer.cpp: $(LOCAL_PATH)/glsl_lexer.ll + $(call local-l-or-ll-to-c-or-cpp) + +$(intermediates)/glsl_parser.cpp: $(LOCAL_PATH)/glsl_parser.yy + $(call local-yy-to-cpp-and-h,.cpp) + +$(intermediates)/glcpp/glcpp-lex.c: $(LOCAL_PATH)/glcpp/glcpp-lex.l + $(call local-l-or-ll-to-c-or-cpp) + +$(intermediates)/glcpp/glcpp-parse.c: $(LOCAL_PATH)/glcpp/glcpp-parse.y + $(call local-y-to-c-and-h) + +BUILTIN_COMPILER := $(BUILD_OUT_EXECUTABLES)/mesa_builtin_compiler$(BUILD_EXECUTABLE_SUFFIX) + +builtin_function_deps := \ + $(LOCAL_PATH)/builtins/tools/generate_builtins.py \ + $(LOCAL_PATH)/builtins/tools/texture_builtins.py \ + $(BUILTIN_COMPILER) \ + $(wildcard $(LOCAL_PATH)/builtins/profiles/*) \ + $(wildcard $(LOCAL_PATH)/builtins/ir/*) + +$(intermediates)/builtin_function.cpp: PRIVATE_SCRIPT := $(MESA_PYTHON2) $(LOCAL_PATH)/builtins/tools/generate_builtins.py +$(intermediates)/builtin_function.cpp: $(builtin_function_deps) + @mkdir -p $(dir $@) + @echo "Gen GLSL: $(PRIVATE_MODULE) <= $(notdir $@)" + $(hide) $(PRIVATE_SCRIPT) $(BUILTIN_COMPILER) > $@ || rm -f $@ diff --git a/src/glsl/Android.mk b/src/glsl/Android.mk new file mode 100644 index 00000000000..d0b3ff3becf --- /dev/null +++ b/src/glsl/Android.mk @@ -0,0 +1,171 @@ +# Mesa 3-D graphics library +# +# Copyright (C) 2010-2011 Chia-I Wu <[email protected]> +# Copyright (C) 2010-2011 LunarG Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# 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. + +# Android.mk for glsl + +LOCAL_PATH := $(call my-dir) + +# from Makefile +LIBGLCPP_SOURCES = \ + glcpp/glcpp-lex.c \ + glcpp/glcpp-parse.c \ + glcpp/pp.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_detect_recursion.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 + +# --------------------------------------- +# Build libmesa_glsl +# --------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(C_SOURCES) \ + $(CXX_SOURCES) \ + builtin_function.cpp + +LOCAL_C_INCLUDES := \ + $(MESA_TOP)/src/mapi \ + $(MESA_TOP)/src/mesa + +LOCAL_MODULE := libmesa_glsl + +include $(LOCAL_PATH)/Android.gen.mk +include $(MESA_COMMON_MK) +include $(BUILD_STATIC_LIBRARY) + +# --------------------------------------- +# Build mesa_builtin_compiler for host +# --------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(C_SOURCES) \ + $(CXX_SOURCES) \ + builtin_stubs.cpp \ + main.cpp \ + standalone_scaffolding.cpp + +LOCAL_C_INCLUDES := \ + $(MESA_TOP)/src/mapi \ + $(MESA_TOP)/src/mesa + +LOCAL_STATIC_LIBRARIES := libmesa_glsl_utils + +LOCAL_MODULE := mesa_builtin_compiler + +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true +include $(LOCAL_PATH)/Android.gen.mk +include $(MESA_COMMON_MK) +include $(BUILD_HOST_EXECUTABLE) + +# --------------------------------------- +# Build glsl_compiler +# --------------------------------------- + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + main.cpp \ + standalone_scaffolding.cpp + +LOCAL_C_INCLUDES := \ + $(MESA_TOP)/src/mapi \ + $(MESA_TOP)/src/mesa + +LOCAL_STATIC_LIBRARIES := libmesa_glsl libmesa_glsl_utils + +LOCAL_MODULE_TAGS := eng +LOCAL_MODULE := glsl_compiler + +include $(MESA_COMMON_MK) +include $(BUILD_EXECUTABLE) diff --git a/src/glsl/Makefile b/src/glsl/Makefile index e0776c1b55d..c20a6c9edd9 100644 --- a/src/glsl/Makefile +++ b/src/glsl/Makefile @@ -39,6 +39,7 @@ CXX_SOURCES = \ ir.cpp \ ir_expression_flattening.cpp \ ir_function_can_inline.cpp \ + ir_function_detect_recursion.cpp \ ir_function.cpp \ ir_hierarchical_visitor.cpp \ ir_hv_accept.cpp \ @@ -88,18 +89,32 @@ CXX_SOURCES = \ LIBS = \ $(TOP)/src/glsl/libglsl.a -APPS = glsl_compiler glcpp/glcpp +APPS = glsl_compiler glsl_test glcpp/glcpp GLSL2_C_SOURCES = \ ../mesa/program/hash_table.c \ ../mesa/program/symbol_table.c GLSL2_CXX_SOURCES = \ - main.cpp + main.cpp \ + standalone_scaffolding.cpp GLSL2_OBJECTS = \ $(GLSL2_C_SOURCES:.c=.o) \ $(GLSL2_CXX_SOURCES:.cpp=.o) +TEST_C_SOURCES = \ + ../mesa/program/hash_table.c \ + ../mesa/program/symbol_table.c + +TEST_CXX_SOURCES = \ + standalone_scaffolding.cpp \ + test.cpp \ + test_optpass.cpp + +TEST_OBJECTS = \ + $(TEST_C_SOURCES:.c=.o) \ + $(TEST_CXX_SOURCES:.cpp=.o) + ### Basic defines ### DEFINES += \ @@ -128,7 +143,9 @@ ALL_SOURCES = \ $(C_SOURCES) \ $(CXX_SOURCES) \ $(GLSL2_CXX_SOURCES) \ - $(GLSL2_C_SOURCES) + $(GLSL2_C_SOURCES) \ + $(TEST_CXX_SOURCES) \ + $(TEST_C_SOURCES) ##### TARGETS ##### @@ -147,10 +164,11 @@ depend: $(ALL_SOURCES) Makefile rm -f depend touch depend $(MKDEP) $(MKDEP_OPTIONS) $(INCLUDES) $(ALL_SOURCES) 2> /dev/null + $(MKDEP) $(MKDEP_OPTIONS) -a -p $(DRICORE_OBJ_DIR)/ $(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 $(GLCPP_OBJECTS) $(GLSL2_OBJECTS) $(TEST_OBJECTS) $(OBJECTS) lib$(LIBNAME).a depend depend.bak builtin_function.cpp builtin_function.o builtin_stubs.o builtin_compiler -rm -f $(APPS) clean-dricore: @@ -173,6 +191,9 @@ install-dricore: default glsl_compiler: $(GLSL2_OBJECTS) libglsl.a builtin_stubs.o $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLSL2_OBJECTS) builtin_stubs.o $(LIBS) -o $@ +glsl_test: $(TEST_OBJECTS) libglsl.a builtin_stubs.o + $(APP_CXX) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(TEST_OBJECTS) builtin_stubs.o $(LIBS) -o $@ + glcpp: glcpp/glcpp glcpp/glcpp: $(GLCPP_OBJECTS) $(APP_CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) $(GLCPP_OBJECTS) -o $@ diff --git a/src/glsl/SConscript b/src/glsl/SConscript index 1441cc74bd8..1da58a91f91 100644 --- a/src/glsl/SConscript +++ b/src/glsl/SConscript @@ -50,6 +50,7 @@ glsl_sources = [ 'ir.cpp', 'ir_expression_flattening.cpp', 'ir_function_can_inline.cpp', + 'ir_function_detect_recursion.cpp', 'ir_function.cpp', 'ir_hierarchical_visitor.cpp', 'ir_hv_accept.cpp', @@ -95,6 +96,7 @@ glsl_sources = [ 'opt_tree_grafting.cpp', 'ralloc.c', 's_expression.cpp', + 'standalone_scaffolding.cpp', 'strtod.c', ] diff --git a/src/glsl/TODO b/src/glsl/TODO index a3762384ff2..c99d7e152d6 100644 --- a/src/glsl/TODO +++ b/src/glsl/TODO @@ -9,11 +9,12 @@ - Implement support for ir_binop_dot in ir_algebraic.cpp. Perform transformations such as "dot(v, vec3(0.0, 1.0, 0.0))" -> v.y. -1.30 features: - -- Implement AST-to-HIR conversion of bit-shift operators. +- Track source locations throughout the IR. There are currently several + places where we cannot emit line numbers for errors (and currently emit 0:0) + because we've "lost" the line number information. This is particularly + noticeable at link time. -- Implement AST-to-HIR conversion of bit-wise {&,|,^,!} operators. +1.30 features: - Implement AST-to-HIR conversion of switch-statements - switch @@ -21,18 +22,6 @@ - Update break to correcly handle mixed nexting of switch-statements and loops. -- Handle currently unsupported constant expression types - - ir_unop_bit_not - - ir_binop_mod - - ir_binop_lshift - - ir_binop_rshift - - ir_binop_bit_and - - ir_binop_bit_xor - - ir_binop_bit_or - -- Implement support for 1.30 style shadow compares which only return a float - instead of a vec4. - - Implement support for gl_ClipDistance. This is non-trivial because gl_ClipDistance is exposed as a float[8], but all hardware actually implements it as vec4[2].
\ No newline at end of file diff --git a/src/glsl/ast.h b/src/glsl/ast.h index 878f48b2070..d1de2271873 100644 --- a/src/glsl/ast.h +++ b/src/glsl/ast.h @@ -730,7 +730,6 @@ _mesa_ast_field_selection_to_hir(const ast_expression *expr, struct _mesa_glsl_parse_state *state); void -emit_function(_mesa_glsl_parse_state *state, exec_list *instructions, - ir_function *f); +emit_function(_mesa_glsl_parse_state *state, ir_function *f); #endif /* AST_H */ diff --git a/src/glsl/ast_function.cpp b/src/glsl/ast_function.cpp index 60a2c617f70..ca45934a478 100644 --- a/src/glsl/ast_function.cpp +++ b/src/glsl/ast_function.cpp @@ -62,8 +62,10 @@ process_parameters(exec_list *instructions, exec_list *actual_parameters, * * \param return_type Return type of the function. May be \c NULL. * \param name Name of the function. - * \param parameters Parameter list for the function. This may be either a - * formal or actual parameter list. Only the type is used. + * \param parameters List of \c ir_instruction nodes representing the + * parameter list for the function. This may be either a + * formal (\c ir_variable) or actual (\c ir_rvalue) + * parameter list. Only the type is used. * * \return * A ralloced string representing the prototype of the function. @@ -123,7 +125,7 @@ match_function_by_name(exec_list *instructions, const char *name, if (f == NULL) { f = new(ctx) ir_function(name); state->symbols->add_global_function(f); - emit_function(state, instructions, f); + emit_function(state, f); } f->add_signature(sig->clone_prototype(f, NULL)); @@ -132,6 +134,8 @@ match_function_by_name(exec_list *instructions, const char *name, } } + exec_list post_call_conversions; + if (sig != NULL) { /* Verify that 'out' and 'inout' actual parameters are lvalues. This * isn't done in ir_function::matching_signature because that function @@ -139,6 +143,12 @@ match_function_by_name(exec_list *instructions, const char *name, * * Also, validate that 'const_in' formal parameters (an extension of our * IR) correspond to ir_constant actual parameters. + * + * Also, perform implicit conversion of arguments. Note: to implicitly + * convert out parameters, we need to place them in a temporary + * variable, and do the conversion after the call takes place. Since we + * haven't emitted the call yet, we'll place the post-call conversions + * in a temporary exec_list, and emit them later. */ exec_list_iterator actual_iter = actual_parameters->iterator(); exec_list_iterator formal_iter = sig->parameters.iterator(); @@ -154,6 +164,7 @@ match_function_by_name(exec_list *instructions, const char *name, _mesa_glsl_error(loc, state, "parameter `%s' must be a constant expression", formal->name); + return ir_call::get_error_instruction(ctx); } if ((formal->mode == ir_var_out) @@ -183,8 +194,64 @@ match_function_by_name(exec_list *instructions, const char *name, } if (formal->type->is_numeric() || formal->type->is_boolean()) { - ir_rvalue *converted = convert_component(actual, formal->type); - actual->replace_with(converted); + switch (formal->mode) { + case ir_var_const_in: + case ir_var_in: { + ir_rvalue *converted + = convert_component(actual, formal->type); + actual->replace_with(converted); + break; + } + case ir_var_out: + if (actual->type != formal->type) { + /* To convert an out parameter, we need to create a + * temporary variable to hold the value before conversion, + * and then perform the conversion after the function call + * returns. + * + * This has the effect of transforming code like this: + * + * void f(out int x); + * float value; + * f(value); + * + * Into IR that's equivalent to this: + * + * void f(out int x); + * float value; + * int out_parameter_conversion; + * f(out_parameter_conversion); + * value = float(out_parameter_conversion); + */ + ir_variable *tmp = + new(ctx) ir_variable(formal->type, + "out_parameter_conversion", + ir_var_temporary); + instructions->push_tail(tmp); + ir_dereference_variable *deref_tmp_1 + = new(ctx) ir_dereference_variable(tmp); + ir_dereference_variable *deref_tmp_2 + = new(ctx) ir_dereference_variable(tmp); + ir_rvalue *converted_tmp + = convert_component(deref_tmp_1, actual->type); + ir_assignment *assignment + = new(ctx) ir_assignment(actual, converted_tmp); + post_call_conversions.push_tail(assignment); + actual->replace_with(deref_tmp_2); + } + break; + case ir_var_inout: + /* Inout parameters should never require conversion, since that + * would require an implicit conversion to exist both to and + * from the formal parameter type, and there are no + * bidirectional implicit conversions. + */ + assert (actual->type == formal->type); + break; + default: + assert (!"Illegal formal parameter mode"); + break; + } } actual_iter.next(); @@ -194,11 +261,27 @@ match_function_by_name(exec_list *instructions, const char *name, /* Always insert the call in the instruction stream, and return a deref * of its return val if it returns a value, since we don't know if * the rvalue is going to be assigned to anything or not. + * + * Also insert any out parameter conversions after the call. */ ir_call *call = new(ctx) ir_call(sig, actual_parameters); + ir_dereference_variable *deref; if (!sig->return_type->is_void()) { + /* If the function call is a constant expression, don't + * generate the instructions to call it; just generate an + * ir_constant representing the constant value. + * + * Function calls can only be constant expressions starting + * in GLSL 1.20. + */ + if (state->language_version >= 120) { + ir_constant *const_val = call->constant_expression_value(); + if (const_val) { + return const_val; + } + } + ir_variable *var; - ir_dereference_variable *deref; var = new(ctx) ir_variable(sig->return_type, ralloc_asprintf(ctx, "%s_retval", @@ -209,15 +292,14 @@ match_function_by_name(exec_list *instructions, const char *name, deref = new(ctx) ir_dereference_variable(var); ir_assignment *assign = new(ctx) ir_assignment(deref, call, NULL); instructions->push_tail(assign); - if (state->language_version >= 120) - var->constant_value = call->constant_expression_value(); deref = new(ctx) ir_dereference_variable(var); - return deref; } else { instructions->push_tail(call); - return NULL; + deref = NULL; } + instructions->append_list(&post_call_conversions); + return deref; } else { char *str = prototype_string(NULL, name, actual_parameters); @@ -440,13 +522,21 @@ process_array_constructor(exec_list *instructions, ir_rvalue *ir = (ir_rvalue *) n; ir_rvalue *result = ir; - /* Apply implicit conversions (not the scalar constructor rules!) */ + /* Apply implicit conversions (not the scalar constructor rules!). See + * the spec quote above. */ if (constructor_type->element_type()->is_float()) { const glsl_type *desired_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, ir->type->vector_elements, ir->type->matrix_columns); - result = convert_component(ir, desired_type); + if (result->type->can_implicitly_convert_to(desired_type)) { + /* Even though convert_component() implements the constructor + * conversion rules (not the implicit conversion rules), its safe + * to use it here because we already checked that the implicit + * conversion is legal. + */ + result = convert_component(ir, desired_type); + } } if (result->type != constructor_type->element_type()) { diff --git a/src/glsl/ast_to_hir.cpp b/src/glsl/ast_to_hir.cpp index 2312c297c40..9e7496b4b43 100644 --- a/src/glsl/ast_to_hir.cpp +++ b/src/glsl/ast_to_hir.cpp @@ -66,6 +66,8 @@ _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) state->current_function = NULL; + state->toplevel_ir = instructions; + /* Section 4.2 of the GLSL 1.20 specification states: * "The built-in functions are scoped in a scope outside the global scope * users declare global variables in. That is, a shader's global scope, @@ -83,6 +85,10 @@ _mesa_ast_to_hir(exec_list *instructions, struct _mesa_glsl_parse_state *state) foreach_list_typed (ast_node, ast, link, & state->translation_unit) ast->hir(instructions, state); + + detect_recursion_unlinked(state, instructions); + + state->toplevel_ir = NULL; } @@ -647,6 +653,16 @@ validate_assignment(struct _mesa_glsl_parse_state *state, return NULL; } +static void +mark_whole_array_access(ir_rvalue *access) +{ + ir_dereference_variable *deref = access->as_dereference_variable(); + + if (deref && deref->var) { + deref->var->max_array_access = deref->type->length - 1; + } +} + ir_rvalue * do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state, ir_rvalue *lhs, ir_rvalue *rhs, bool is_initializer, @@ -707,6 +723,7 @@ do_assignment(exec_list *instructions, struct _mesa_glsl_parse_state *state, rhs->type->array_size()); d->type = var->type; } + mark_whole_array_access(lhs); } /* Most callers of do_assignment (assign, add_assign, pre_inc/dec, @@ -767,16 +784,6 @@ ast_node::hir(exec_list *instructions, 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) { @@ -1763,11 +1770,6 @@ process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size, 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"); @@ -1784,6 +1786,14 @@ process_array_type(YYLTYPE *loc, const glsl_type *base, ast_node *array_size, } else { assert(size->type == ir->type); length = size->value.u[0]; + + /* If the array size is const (and we've verified that + * it is) then no instructions should have been emitted + * when we converted it to HIR. If they were emitted, + * then either the array size isn't const after all, or + * we are emitting unnecessary instructions. + */ + assert(dummy_instructions.is_empty()); } } } @@ -2397,12 +2407,12 @@ ast_declarator_list::hir(exec_list *instructions, 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) { + /* Warn if this empty declaration is not for declaring a structure. + */ + if (this->type->specifier->structure == NULL) { + _mesa_glsl_warning(&loc, state, "empty declaration"); + } } else { _mesa_glsl_error(& loc, state, "incomplete declaration"); } @@ -2924,23 +2934,16 @@ ast_parameter_declarator::parameters_to_hir(exec_list *ast_parameters, void -emit_function(_mesa_glsl_parse_state *state, exec_list *instructions, - ir_function *f) +emit_function(_mesa_glsl_parse_state *state, 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 invariants disallow function declarations or definitions + * nested within other function definitions. But there is no + * requirement about the relative order of function declarations + * and definitions with respect to one another. So simply insert + * the new ir_function block at the end of the toplevel instruction + * list. + */ + state->toplevel_ir->push_tail(f); } @@ -3067,7 +3070,7 @@ ast_function::hir(exec_list *instructions, return NULL; } - emit_function(state, instructions, f); + emit_function(state, f); } /* Verify the return type of main() */ diff --git a/src/glsl/builtins/ir/asin b/src/glsl/builtins/ir/asin index e230ad614ee..45d9e672958 100644 --- a/src/glsl/builtins/ir/asin +++ b/src/glsl/builtins/ir/asin @@ -5,23 +5,26 @@ ((return (expression float * (expression float sign (var_ref x)) (expression float - - (expression float * - (constant float (3.1415926)) - (constant float (0.5))) + (constant float (1.5707964)) (expression float * (expression float sqrt (expression float - (constant float (1.0)) (expression float abs (var_ref x)))) (expression float + - (constant float (1.5707288)) + (constant float (1.5707964)) (expression float * (expression float abs (var_ref x)) (expression float + - (constant float (-0.2121144)) + (constant float (-0.21460183)) (expression float * - (constant float (0.0742610)) - (expression float abs (var_ref x)))))))))))) + (expression float abs (var_ref x)) + (expression float + + (constant float (0.086566724)) + (expression float * + (expression float abs (var_ref x)) + (constant float (-0.03102955)) + )))))))))))) (signature vec2 (parameters @@ -29,23 +32,26 @@ ((return (expression vec2 * (expression vec2 sign (var_ref x)) (expression vec2 - - (expression float * - (constant float (3.1415926)) - (constant float (0.5))) + (constant float (1.5707964)) (expression vec2 * (expression vec2 sqrt (expression vec2 - (constant float (1.0)) (expression vec2 abs (var_ref x)))) (expression vec2 + - (constant float (1.5707288)) + (constant float (1.5707964)) (expression vec2 * (expression vec2 abs (var_ref x)) (expression vec2 + - (constant float (-0.2121144)) + (constant float (-0.21460183)) (expression vec2 * - (constant float (0.0742610)) - (expression vec2 abs (var_ref x)))))))))))) + (expression vec2 abs (var_ref x)) + (expression vec2 + + (constant float (0.086566724)) + (expression vec2 * + (expression vec2 abs (var_ref x)) + (constant float (-0.03102955)) + )))))))))))) (signature vec3 (parameters @@ -53,23 +59,26 @@ ((return (expression vec3 * (expression vec3 sign (var_ref x)) (expression vec3 - - (expression float * - (constant float (3.1415926)) - (constant float (0.5))) + (constant float (1.5707964)) (expression vec3 * (expression vec3 sqrt (expression vec3 - (constant float (1.0)) (expression vec3 abs (var_ref x)))) (expression vec3 + - (constant float (1.5707288)) + (constant float (1.5707964)) (expression vec3 * (expression vec3 abs (var_ref x)) (expression vec3 + - (constant float (-0.2121144)) + (constant float (-0.21460183)) (expression vec3 * - (constant float (0.0742610)) - (expression vec3 abs (var_ref x)))))))))))) + (expression vec3 abs (var_ref x)) + (expression vec3 + + (constant float (0.086566724)) + (expression vec3 * + (expression vec3 abs (var_ref x)) + (constant float (-0.03102955)) + )))))))))))) (signature vec4 (parameters @@ -77,21 +86,24 @@ ((return (expression vec4 * (expression vec4 sign (var_ref x)) (expression vec4 - - (expression float * - (constant float (3.1415926)) - (constant float (0.5))) + (constant float (1.5707964)) (expression vec4 * (expression vec4 sqrt (expression vec4 - (constant float (1.0)) (expression vec4 abs (var_ref x)))) (expression vec4 + - (constant float (1.5707288)) + (constant float (1.5707964)) (expression vec4 * (expression vec4 abs (var_ref x)) (expression vec4 + - (constant float (-0.2121144)) + (constant float (-0.21460183)) (expression vec4 * - (constant float (0.0742610)) - (expression vec4 abs (var_ref x)))))))))))) + (expression vec4 abs (var_ref x)) + (expression vec4 + + (constant float (0.086566724)) + (expression vec4 * + (expression vec4 abs (var_ref x)) + (constant float (-0.03102955)) + )))))))))))) )) diff --git a/src/glsl/builtins/ir/atan b/src/glsl/builtins/ir/atan index cfecc1f1749..7b5ea13c6ba 100644 --- a/src/glsl/builtins/ir/atan +++ b/src/glsl/builtins/ir/atan @@ -54,7 +54,9 @@ ) ( (declare () float r) - (if (expression bool > (expression float abs (var_ref x)) (constant float (0.000100))) ( + (if (expression bool > + (expression float abs (var_ref x)) + (expression float * (constant float (1.0e-8)) (expression float abs (var_ref y)))) ( (assign (x) (var_ref r) (call atan ((expression float / (var_ref y) (var_ref x))))) (if (expression bool < (var_ref x) (constant float (0.000000)) ) ( (if (expression bool >= (var_ref y) (constant float (0.000000)) ) diff --git a/src/glsl/builtins/ir/radians b/src/glsl/builtins/ir/radians index 6a0f5d2e219..a419101cf16 100644 --- a/src/glsl/builtins/ir/radians +++ b/src/glsl/builtins/ir/radians @@ -2,20 +2,20 @@ (signature float (parameters (declare (in) float arg0)) - ((return (expression float * (var_ref arg0) (constant float (0.017453)))))) + ((return (expression float * (var_ref arg0) (constant float (0.0174532925)))))) (signature vec2 (parameters (declare (in) vec2 arg0)) - ((return (expression vec2 * (var_ref arg0) (constant float (0.017453)))))) + ((return (expression vec2 * (var_ref arg0) (constant float (0.0174532925)))))) (signature vec3 (parameters (declare (in) vec3 arg0)) - ((return (expression vec3 * (var_ref arg0) (constant float (0.017453)))))) + ((return (expression vec3 * (var_ref arg0) (constant float (0.0174532925)))))) (signature vec4 (parameters (declare (in) vec4 arg0)) - ((return (expression vec4 * (var_ref arg0) (constant float (0.017453)))))) + ((return (expression vec4 * (var_ref arg0) (constant float (0.0174532925)))))) )) diff --git a/src/glsl/builtins/profiles/130.frag b/src/glsl/builtins/profiles/130.frag index 0e3c7ac4199..c121859f14c 100644 --- a/src/glsl/builtins/profiles/130.frag +++ b/src/glsl/builtins/profiles/130.frag @@ -465,7 +465,6 @@ bvec4 not(bvec4 x); * 8.7 - Texture Lookup Functions */ -#if 0 /* textureSize */ int textureSize( sampler1D sampler, int lod); int textureSize(isampler1D sampler, int lod); @@ -496,7 +495,6 @@ ivec3 textureSize(usampler2DArray sampler, int lod); ivec2 textureSize(sampler1DArrayShadow sampler, int lod); ivec3 textureSize(sampler2DArrayShadow sampler, int lod); -#endif /* texture - no bias */ vec4 texture( sampler1D sampler, float P); diff --git a/src/glsl/builtins/profiles/130.vert b/src/glsl/builtins/profiles/130.vert index f85b27f8f8c..ebd9a508851 100644 --- a/src/glsl/builtins/profiles/130.vert +++ b/src/glsl/builtins/profiles/130.vert @@ -467,7 +467,6 @@ bvec4 not(bvec4 x); * 8.7 - Texture Lookup Functions */ -#if 0 /* textureSize */ int textureSize( sampler1D sampler, int lod); int textureSize(isampler1D sampler, int lod); @@ -498,7 +497,6 @@ ivec3 textureSize(usampler2DArray sampler, int lod); ivec2 textureSize(sampler1DArrayShadow sampler, int lod); ivec3 textureSize(sampler2DArrayShadow sampler, int lod); -#endif /* texture - no bias */ vec4 texture( sampler1D sampler, float P); diff --git a/src/glsl/builtins/tools/texture_builtins.py b/src/glsl/builtins/tools/texture_builtins.py index a4054caac98..7e569bf562b 100755 --- a/src/glsl/builtins/tools/texture_builtins.py +++ b/src/glsl/builtins/tools/texture_builtins.py @@ -44,6 +44,11 @@ def get_extra_dim(sampler_type, use_proj, unused_fields): extra_dim += 1 return extra_dim +def get_txs_dim(sampler_type): + if sampler_type.startswith("Cube"): + return 2 + return get_coord_dim(sampler_type) + def generate_sigs(g, tex_inst, sampler_type, variant = 0, unused_fields = 0): coord_dim = get_coord_dim(sampler_type) extra_dim = get_extra_dim(sampler_type, variant & Proj, unused_fields) @@ -51,17 +56,20 @@ def generate_sigs(g, tex_inst, sampler_type, variant = 0, unused_fields = 0): if variant & Single: return_type = "float" + elif tex_inst == "txs": + return_type = vec_type("i", get_txs_dim(sampler_type)) else: return_type = g + "vec4" # Print parameters print " (signature", return_type print " (parameters" - print " (declare (in) " + g + "sampler" + sampler_type + " sampler)" - print " (declare (in) " + vec_type("i" if tex_inst == "txf" else "", coord_dim + extra_dim) + " P)", + print " (declare (in) " + g + "sampler" + sampler_type + " sampler)", + if tex_inst != "txs": + print "\n (declare (in) " + vec_type("i" if tex_inst == "txf" else "", coord_dim + extra_dim) + " P)", if tex_inst == "txl": print "\n (declare (in) float lod)", - elif tex_inst == "txf": + elif tex_inst == "txf" or tex_inst == "txs": print "\n (declare (in) int lod)", elif tex_inst == "txd": grad_type = vec_type("", coord_dim) @@ -75,18 +83,19 @@ def generate_sigs(g, tex_inst, sampler_type, variant = 0, unused_fields = 0): print ")\n ((return (" + tex_inst, return_type, "(var_ref sampler)", - # Coordinate - if extra_dim > 0: - print "(swiz " + "xyzw"[:coord_dim] + " (var_ref P))", - else: - print "(var_ref P)", + if tex_inst != "txs": + # Coordinate + if extra_dim > 0: + print "(swiz " + "xyzw"[:coord_dim] + " (var_ref P))", + else: + print "(var_ref P)", - if variant & Offset: - print "(var_ref offset)", - else: - print "0", + if variant & Offset: + print "(var_ref offset)", + else: + print "0", - if tex_inst != "txf": + if tex_inst != "txf" and tex_inst != "txs": # Projective divisor if variant & Proj: print "(swiz " + "xyzw"[coord_dim + extra_dim-1] + " (var_ref P))", @@ -104,7 +113,7 @@ def generate_sigs(g, tex_inst, sampler_type, variant = 0, unused_fields = 0): # Bias/explicit LOD/gradient: if tex_inst == "txb": print "(var_ref bias)", - elif tex_inst == "txl" or tex_inst == "txf": + elif tex_inst == "txl" or tex_inst == "txf" or tex_inst == "txs": print "(var_ref lod)", elif tex_inst == "txd": print "((var_ref dPdx) (var_ref dPdy))", @@ -130,6 +139,19 @@ def end_function(fs, name): # # Takes a dictionary as an argument. def generate_texture_functions(fs): + start_function("textureSize") + generate_fiu_sigs("txs", "1D") + generate_fiu_sigs("txs", "2D") + generate_fiu_sigs("txs", "3D") + generate_fiu_sigs("txs", "Cube") + generate_fiu_sigs("txs", "1DArray") + generate_fiu_sigs("txs", "2DArray") + generate_sigs("", "txs", "1DShadow") + generate_sigs("", "txs", "2DShadow") + generate_sigs("", "txs", "1DArrayShadow") + generate_sigs("", "txs", "2DArrayShadow") + end_function(fs, "textureSize") + start_function("texture") generate_fiu_sigs("tex", "1D") generate_fiu_sigs("tex", "2D") diff --git a/src/glsl/glcpp/glcpp-parse.y b/src/glsl/glcpp/glcpp-parse.y index 0a35e88cec7..940830416c0 100644 --- a/src/glsl/glcpp/glcpp-parse.y +++ b/src/glsl/glcpp/glcpp-parse.y @@ -1132,8 +1132,10 @@ glcpp_parser_create (const struct gl_extensions *extensions, int api) if (extensions->ARB_shader_texture_lod) add_builtin_define(parser, "GL_ARB_shader_texture_lod", 1); - if (extensions->AMD_conservative_depth) + if (extensions->AMD_conservative_depth) { add_builtin_define(parser, "GL_AMD_conservative_depth", 1); + add_builtin_define(parser, "GL_ARB_conservative_depth", 1); + } } language_version = 110; diff --git a/src/glsl/glsl_parser.yy b/src/glsl/glsl_parser.yy index 2c0498ece7a..25d02fb1eaf 100644 --- a/src/glsl/glsl_parser.yy +++ b/src/glsl/glsl_parser.yy @@ -971,13 +971,9 @@ single_declaration: fully_specified_type { void *ctx = state; - if ($1->specifier->type_specifier != ast_struct) { - _mesa_glsl_error(& @1, state, "empty declaration list\n"); - YYERROR; - } else { - $$ = new(ctx) ast_declarator_list($1); - $$->set_location(yylloc); - } + /* Empty declaration list is valid. */ + $$ = new(ctx) ast_declarator_list($1); + $$->set_location(yylloc); } | fully_specified_type any_identifier { @@ -1115,7 +1111,7 @@ layout_qualifier_id: } } - /* Layout qualifiers for AMD_conservative_depth. */ + /* Layout qualifiers for AMD/ARB_conservative_depth. */ if (!got_one && state->AMD_conservative_depth_enable) { if (strcmp($1, "depth_any") == 0) { got_one = true; @@ -1133,7 +1129,7 @@ layout_qualifier_id: if (got_one && state->AMD_conservative_depth_warn) { _mesa_glsl_warning(& @1, state, - "GL_AMD_conservative_depth " + "GL_ARB_conservative_depth " "layout qualifier `%s' is used\n", $1); } } diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp index cc781378d76..8f740e6a8e9 100644 --- a/src/glsl/glsl_parser_extras.cpp +++ b/src/glsl/glsl_parser_extras.cpp @@ -253,6 +253,7 @@ struct _mesa_glsl_extension { static const _mesa_glsl_extension _mesa_glsl_supported_extensions[] = { /* target availability API availability */ /* name VS GS FS GL ES supported flag */ + EXT(ARB_conservative_depth, true, false, true, true, false, AMD_conservative_depth), EXT(ARB_draw_buffers, false, false, true, true, false, dummy_true), EXT(ARB_draw_instanced, true, false, false, true, false, ARB_draw_instanced), EXT(ARB_explicit_attrib_location, true, false, true, true, false, ARB_explicit_attrib_location), diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h index 2f4d3cba77f..dc6911d1c9a 100644 --- a/src/glsl/glsl_parser_extras.h +++ b/src/glsl/glsl_parser_extras.h @@ -129,6 +129,12 @@ struct _mesa_glsl_parse_state { */ class ir_function_signature *current_function; + /** + * During AST to IR conversion, pointer to the toplevel IR + * instruction list being generated. + */ + exec_list *toplevel_ir; + /** Have we found a return statement in this function? */ bool found_return; @@ -174,6 +180,8 @@ struct _mesa_glsl_parse_state { bool ARB_shader_stencil_export_warn; bool AMD_conservative_depth_enable; bool AMD_conservative_depth_warn; + bool ARB_conservative_depth_enable; + bool ARB_conservative_depth_warn; bool AMD_shader_stencil_export_enable; bool AMD_shader_stencil_export_warn; bool OES_texture_3D_enable; diff --git a/src/glsl/glsl_types.cpp b/src/glsl/glsl_types.cpp index a5e21bbb96c..c94aec0d2da 100644 --- a/src/glsl/glsl_types.cpp +++ b/src/glsl/glsl_types.cpp @@ -523,3 +523,19 @@ glsl_type::component_slots() const return 0; } } + +bool +glsl_type::can_implicitly_convert_to(const glsl_type *desired) const +{ + if (this == desired) + return true; + + /* There is no conversion among matrix types. */ + if (this->matrix_columns > 1 || desired->matrix_columns > 1) + return false; + + /* int and uint can be converted to float. */ + return desired->is_float() + && this->is_integer() + && this->vector_elements == desired->vector_elements; +} diff --git a/src/glsl/glsl_types.h b/src/glsl/glsl_types.h index 87f57e7c756..048696693be 100644 --- a/src/glsl/glsl_types.h +++ b/src/glsl/glsl_types.h @@ -224,6 +224,41 @@ struct glsl_type { */ unsigned component_slots() const; + /** + * \brief Can this type be implicitly converted to another? + * + * \return True if the types are identical or if this type can be converted + * to \c desired according to Section 4.1.10 of the GLSL spec. + * + * \verbatim + * From page 25 (31 of the pdf) of the GLSL 1.50 spec, Section 4.1.10 + * Implicit Conversions: + * + * In some situations, an expression and its type will be implicitly + * converted to a different type. The following table shows all allowed + * implicit conversions: + * + * Type of expression | Can be implicitly converted to + * -------------------------------------------------- + * int float + * uint + * + * ivec2 vec2 + * uvec2 + * + * ivec3 vec3 + * uvec3 + * + * ivec4 vec4 + * uvec4 + * + * 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. + * \endverbatim + */ + bool can_implicitly_convert_to(const glsl_type *desired) const; /** * Query whether or not a type is a scalar (non-vector and non-matrix). diff --git a/src/glsl/ir.cpp b/src/glsl/ir.cpp index 827fe8e17a7..41ed4f114ca 100644 --- a/src/glsl/ir.cpp +++ b/src/glsl/ir.cpp @@ -1096,7 +1096,7 @@ ir_dereference_record::ir_dereference_record(ir_variable *var, } bool -ir_dereference::is_lvalue() +ir_dereference::is_lvalue() const { ir_variable *var = this->variable_referenced(); @@ -1121,7 +1121,7 @@ ir_dereference::is_lvalue() } -const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf" }; +const char *tex_opcode_strs[] = { "tex", "txb", "txl", "txd", "txf", "txs" }; const char *ir_texture::opcode_string() { @@ -1150,11 +1150,15 @@ ir_texture::set_sampler(ir_dereference *sampler, const glsl_type *type) 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); + if (this->op == ir_txs) { + assert(type->base_type == GLSL_TYPE_INT); + } else { + assert(sampler->type->sampler_type == (int) type->base_type); + if (sampler->type->sampler_shadow) + assert(type->vector_elements == 4 || type->vector_elements == 1); + else + assert(type->vector_elements == 4); + } } @@ -1310,7 +1314,7 @@ ir_swizzle::create(ir_rvalue *val, const char *str, unsigned vector_length) #undef I ir_variable * -ir_swizzle::variable_referenced() +ir_swizzle::variable_referenced() const { return this->val->variable_referenced(); } diff --git a/src/glsl/ir.h b/src/glsl/ir.h index 80ad3dd295e..2e899f3ed6f 100644 --- a/src/glsl/ir.h +++ b/src/glsl/ir.h @@ -144,7 +144,7 @@ public: ir_rvalue *as_rvalue_to_saturate(); - virtual bool is_lvalue() + virtual bool is_lvalue() const { return false; } @@ -152,7 +152,7 @@ public: /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced() + virtual ir_variable *variable_referenced() const { return NULL; } @@ -236,7 +236,7 @@ enum ir_variable_interpolation { /** * \brief Layout qualifiers for gl_FragDepth. * - * The AMD_conservative_depth extension allows gl_FragDepth to be redeclared + * The AMD/ARB_conservative_depth extensions allow gl_FragDepth to be redeclared * with a layout qualifier. */ enum ir_depth_layout { @@ -1212,7 +1212,8 @@ enum ir_texture_opcode { ir_txb, /**< Texture look-up with LOD bias */ ir_txl, /**< Texture look-up with explicit LOD */ ir_txd, /**< Texture look-up with partial derivatvies */ - ir_txf /**< Texel fetch with explicit LOD */ + ir_txf, /**< Texel fetch with explicit LOD */ + ir_txs /**< Texture size */ }; @@ -1233,6 +1234,7 @@ enum ir_texture_opcode { * (txl <type> <sampler> <coordinate> 0 1 ( ) <lod>) * (txd <type> <sampler> <coordinate> 0 1 ( ) (dPdx dPdy)) * (txf <type> <sampler> <coordinate> 0 <lod>) + * (txs <type> <sampler> <lod>) */ class ir_texture : public ir_rvalue { public: @@ -1355,7 +1357,7 @@ public: virtual ir_visitor_status accept(ir_hierarchical_visitor *); - bool is_lvalue() + bool is_lvalue() const { return val->is_lvalue() && !mask.has_duplicates; } @@ -1363,7 +1365,7 @@ public: /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced(); + virtual ir_variable *variable_referenced() const; ir_rvalue *val; ir_swizzle_mask mask; @@ -1387,12 +1389,12 @@ public: return this; } - bool is_lvalue(); + bool is_lvalue() const; /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced() = 0; + virtual ir_variable *variable_referenced() const = 0; }; @@ -1413,7 +1415,7 @@ public: /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced() + virtual ir_variable *variable_referenced() const { return this->var; } @@ -1462,7 +1464,7 @@ public: /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced() + virtual ir_variable *variable_referenced() const { return this->array->variable_referenced(); } @@ -1496,7 +1498,7 @@ public: /** * Get the variable that is ultimately referenced by an r-value */ - virtual ir_variable *variable_referenced() + virtual ir_variable *variable_referenced() const { return this->record->variable_referenced(); } @@ -1635,6 +1637,32 @@ visit_exec_list(exec_list *list, ir_visitor *visitor); */ void validate_ir_tree(exec_list *instructions); +struct _mesa_glsl_parse_state; +struct gl_shader_program; + +/** + * Detect whether an unlinked shader contains static recursion + * + * If the list of instructions is determined to contain static recursion, + * \c _mesa_glsl_error will be called to emit error messages for each function + * that is in the recursion cycle. + */ +void +detect_recursion_unlinked(struct _mesa_glsl_parse_state *state, + exec_list *instructions); + +/** + * Detect whether a linked shader contains static recursion + * + * If the list of instructions is determined to contain static recursion, + * \c link_error_printf will be called to emit error messages for each function + * that is in the recursion cycle. In addition, + * \c gl_shader_program::LinkStatus will be set to false. + */ +void +detect_recursion_linked(struct gl_shader_program *prog, + exec_list *instructions); + /** * Make a clone of each IR instruction in a list * @@ -1669,4 +1697,8 @@ ir_has_call(ir_instruction *ir); extern void do_set_program_inouts(exec_list *instructions, struct gl_program *prog); +extern char * +prototype_string(const glsl_type *return_type, const char *name, + exec_list *parameters); + #endif /* IR_H */ diff --git a/src/glsl/ir_clone.cpp b/src/glsl/ir_clone.cpp index 069bb85e8de..f0757365dd2 100644 --- a/src/glsl/ir_clone.cpp +++ b/src/glsl/ir_clone.cpp @@ -222,7 +222,8 @@ ir_texture::clone(void *mem_ctx, struct hash_table *ht) const new_tex->type = this->type; new_tex->sampler = this->sampler->clone(mem_ctx, ht); - new_tex->coordinate = this->coordinate->clone(mem_ctx, ht); + if (this->coordinate) + new_tex->coordinate = this->coordinate->clone(mem_ctx, ht); if (this->projector) new_tex->projector = this->projector->clone(mem_ctx, ht); if (this->shadow_comparitor) { @@ -240,6 +241,7 @@ ir_texture::clone(void *mem_ctx, struct hash_table *ht) const break; case ir_txl: case ir_txf: + case ir_txs: new_tex->lod_info.lod = this->lod_info.lod->clone(mem_ctx, ht); break; case ir_txd: diff --git a/src/glsl/ir_function.cpp b/src/glsl/ir_function.cpp index 0f2f1a0eea4..51d32b46f98 100644 --- a/src/glsl/ir_function.cpp +++ b/src/glsl/ir_function.cpp @@ -24,73 +24,28 @@ #include "glsl_types.h" #include "ir.h" -int -type_compare(const glsl_type *a, const glsl_type *b) -{ - /* If the types are the same, they trivially match. - */ - if (a == b) - return 0; - - switch (a->base_type) { - case GLSL_TYPE_UINT: - case GLSL_TYPE_INT: - case GLSL_TYPE_BOOL: - /* There is no implicit conversion to or from integer types or bool. - */ - if ((a->is_integer() != b->is_integer()) - || (a->is_boolean() != b->is_boolean())) - return -1; - - /* FALLTHROUGH */ - - case GLSL_TYPE_FLOAT: - if ((a->vector_elements != b->vector_elements) - || (a->matrix_columns != b->matrix_columns)) - return -1; - - return 1; - - case GLSL_TYPE_SAMPLER: - case GLSL_TYPE_STRUCT: - /* Samplers and structures must match exactly. - */ - return -1; - - case GLSL_TYPE_ARRAY: - if ((b->base_type != GLSL_TYPE_ARRAY) - || (a->length != b->length)) - return -1; - - /* From GLSL 1.50 spec, page 27 (page 33 of the PDF): - * "There are no implicit array or structure conversions." - * - * If the comparison of the array element types detects that a conversion - * would be required, the array types do not match. - */ - return (type_compare(a->fields.array, b->fields.array) == 0) ? 0 : -1; - - case GLSL_TYPE_VOID: - case GLSL_TYPE_ERROR: - default: - /* These are all error conditions. It is invalid for a parameter to - * a function to be declared as error, void, or a function. - */ - return -1; - } - - /* This point should be unreachable. - */ - assert(0); -} - - -static int +typedef enum { + PARAMETER_LIST_NO_MATCH, + PARAMETER_LIST_EXACT_MATCH, + PARAMETER_LIST_INEXACT_MATCH /*< Match requires implicit conversion. */ +} parameter_list_match_t; + +/** + * \brief Check if two parameter lists match. + * + * \param list_a Parameters of the function definition. + * \param list_b Actual parameters passed to the function. + * \see matching_signature() + */ +static parameter_list_match_t parameter_lists_match(const exec_list *list_a, const exec_list *list_b) { const exec_node *node_a = list_a->head; const exec_node *node_b = list_b->head; - int total_score = 0; + + /* This is set to true if there is an inexact match requiring an implicit + * conversion. */ + bool inexact_match = false; for (/* empty */ ; !node_a->is_tail_sentinel() @@ -100,18 +55,17 @@ parameter_lists_match(const exec_list *list_a, const exec_list *list_b) * do not match. */ if (node_b->is_tail_sentinel()) - return -1; + return PARAMETER_LIST_NO_MATCH; const ir_variable *const param = (ir_variable *) node_a; const ir_instruction *const actual = (ir_instruction *) node_b; - /* Determine whether or not the types match. If the types are an - * exact match, the match score is zero. If the types don't match - * but the actual parameter can be coerced to the type of the declared - * parameter, the match score is one. - */ - int score; + if (param->type == actual->type) + continue; + + /* Try to find an implicit conversion from actual to param. */ + inexact_match = true; switch ((enum ir_variable_mode)(param->mode)) { case ir_var_auto: case ir_var_uniform: @@ -121,15 +75,17 @@ parameter_lists_match(const exec_list *list_a, const exec_list *list_b) * as uniform. */ assert(0); - return -1; + return PARAMETER_LIST_NO_MATCH; case ir_var_const_in: case ir_var_in: - score = type_compare(param->type, actual->type); + if (!actual->type->can_implicitly_convert_to(param->type)) + return PARAMETER_LIST_NO_MATCH; break; case ir_var_out: - score = type_compare(actual->type, param->type); + if (!param->type->can_implicitly_convert_to(actual->type)) + return PARAMETER_LIST_NO_MATCH; break; case ir_var_inout: @@ -137,17 +93,12 @@ parameter_lists_match(const exec_list *list_a, const exec_list *list_b) * there is int -> float but no float -> int), inout parameters must * be exact matches. */ - score = (type_compare(actual->type, param->type) == 0) ? 0 : -1; - break; + return PARAMETER_LIST_NO_MATCH; default: assert(false); + return PARAMETER_LIST_NO_MATCH; } - - if (score < 0) - return -1; - - total_score += score; } /* If all of the parameters from the other parameter list have been @@ -155,9 +106,12 @@ parameter_lists_match(const exec_list *list_a, const exec_list *list_b) * match. */ if (!node_b->is_tail_sentinel()) - return -1; + return PARAMETER_LIST_NO_MATCH; - return total_score; + if (inexact_match) + return PARAMETER_LIST_INEXACT_MATCH; + else + return PARAMETER_LIST_EXACT_MATCH; } @@ -181,18 +135,20 @@ ir_function::matching_signature(const exec_list *actual_parameters) ir_function_signature *const sig = (ir_function_signature *) iter.get(); - const int score = parameter_lists_match(& sig->parameters, - actual_parameters); - - /* If we found an exact match, simply return it */ - if (score == 0) + switch (parameter_lists_match(& sig->parameters, actual_parameters)) { + case PARAMETER_LIST_EXACT_MATCH: return sig; - - if (score > 0) { + case PARAMETER_LIST_INEXACT_MATCH: if (match == NULL) match = sig; else multiple_inexact_matches = true; + continue; + case PARAMETER_LIST_NO_MATCH: + continue; + default: + assert(false); + return NULL; } } diff --git a/src/glsl/ir_function_detect_recursion.cpp b/src/glsl/ir_function_detect_recursion.cpp new file mode 100644 index 00000000000..8f805bf1ba9 --- /dev/null +++ b/src/glsl/ir_function_detect_recursion.cpp @@ -0,0 +1,370 @@ +/* + * Copyright © 2011 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_function_detect_recursion.cpp + * Determine whether a shader contains static recursion. + * + * Consider the (possibly disjoint) graph of function calls in a shader. If a + * program contains recursion, this graph will contain a cycle. If a function + * is part of a cycle, it will have a caller and it will have a callee (it + * calls another function). + * + * To detect recursion, the function call graph is constructed. The graph is + * repeatedly reduced by removing any function that either has no callees + * (leaf functions) or has no caller. Eventually the only functions that + * remain will be the functions in the cycles. + * + * The GLSL spec is a bit wishy-washy about recursion. + * + * From page 39 (page 45 of the PDF) of the GLSL 1.10 spec: + * + * "Behavior is undefined if recursion is used. Recursion means having any + * function appearing more than once at any one time in the run-time stack + * of function calls. That is, a function may not call itself either + * directly or indirectly. Compilers may give diagnostic messages when + * this is detectable at compile time, but not all such cases can be + * detected at compile time." + * + * From page 79 (page 85 of the PDF): + * + * "22) Should recursion be supported? + * + * DISCUSSION: Probably not necessary, but another example of limiting + * the language based on how it would directly map to hardware. One + * thought is that recursion would benefit ray tracing shaders. On the + * other hand, many recursion operations can also be implemented with the + * user managing the recursion through arrays. RenderMan doesn't support + * recursion. This could be added at a later date, if it proved to be + * necessary. + * + * RESOLVED on September 10, 2002: Implementations are not required to + * support recursion. + * + * CLOSED on September 10, 2002." + * + * From page 79 (page 85 of the PDF): + * + * "56) Is it an error for an implementation to support recursion if the + * specification says recursion is not supported? + * + * ADDED on September 10, 2002. + * + * DISCUSSION: This issues is related to Issue (22). If we say that + * recursion (or some other piece of functionality) is not supported, is + * it an error for an implementation to support it? Perhaps the + * specification should remain silent on these kind of things so that they + * could be gracefully added later as an extension or as part of the + * standard. + * + * RESOLUTION: Languages, in general, have programs that are not + * well-formed in ways a compiler cannot detect. Portability is only + * ensured for well-formed programs. Detecting recursion is an example of + * this. The language will say a well-formed program may not recurse, but + * compilers are not forced to detect that recursion may happen. + * + * CLOSED: November 29, 2002." + * + * In GLSL 1.10 the behavior of recursion is undefined. Compilers don't have + * to reject shaders (at compile-time or link-time) that contain recursion. + * Instead they could work, or crash, or kill a kitten. + * + * From page 44 (page 50 of the PDF) of the GLSL 1.20 spec: + * + * "Recursion is not allowed, not even statically. Static recursion is + * present if the static function call graph of the program contains + * cycles." + * + * This langauge clears things up a bit, but it still leaves a lot of + * questions unanswered. + * + * - Is the error generated at compile-time or link-time? + * + * - Is it an error to have a recursive function that is never statically + * called by main or any function called directly or indirectly by main? + * Technically speaking, such a function is not in the "static function + * call graph of the program" at all. + * + * \bug + * If a shader has multiple cycles, this algorithm may erroneously complain + * about functions that aren't in any cycle, but are in the part of the call + * tree that connects them. For example, if the call graph consists of a + * cycle between A and B, and a cycle between D and E, and B also calls C + * which calls D, then this algorithm will report C as a function which "has + * static recursion" even though it is not part of any cycle. + * + * A better algorithm for cycle detection that doesn't have this drawback can + * be found here: + * + * http://en.wikipedia.org/wiki/Tarjan%E2%80%99s_strongly_connected_components_algorithm + * + * \author Ian Romanick <[email protected]> + */ +#include "main/core.h" +#include "ir.h" +#include "glsl_parser_extras.h" +#include "linker.h" +#include "program/hash_table.h" +#include "program.h" + +struct call_node : public exec_node { + class function *func; +}; + +class function { +public: + function(ir_function_signature *sig) + : sig(sig) + { + /* empty */ + } + + + /* 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 *node; + + node = ralloc_size(ctx, size); + assert(node != NULL); + + return node; + } + + /* If the user *does* call delete, that's OK, we will just + * ralloc_free in that case. */ + static void operator delete(void *node) + { + ralloc_free(node); + } + + ir_function_signature *sig; + + /** List of functions called by this function. */ + exec_list callees; + + /** List of functions that call this function. */ + exec_list callers; +}; + +class has_recursion_visitor : public ir_hierarchical_visitor { +public: + has_recursion_visitor() + : current(NULL) + { + this->mem_ctx = ralloc_context(NULL); + this->function_hash = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + } + + ~has_recursion_visitor() + { + hash_table_dtor(this->function_hash); + ralloc_free(this->mem_ctx); + } + + function *get_function(ir_function_signature *sig) + { + function *f = (function *) hash_table_find(this->function_hash, sig); + if (f == NULL) { + f = new(mem_ctx) function(sig); + hash_table_insert(this->function_hash, f, sig); + } + + return f; + } + + virtual ir_visitor_status visit_enter(ir_function_signature *sig) + { + this->current = this->get_function(sig); + return visit_continue; + } + + virtual ir_visitor_status visit_leave(ir_function_signature *sig) + { + (void) sig; + this->current = NULL; + return visit_continue; + } + + virtual ir_visitor_status visit_enter(ir_call *call) + { + /* At global scope this->current will be NULL. Since there is no way to + * call global scope, it can never be part of a cycle. Don't bother + * adding calls from global scope to the graph. + */ + if (this->current == NULL) + return visit_continue; + + function *const target = this->get_function(call->get_callee()); + + /* Create a link from the caller to the callee. + */ + call_node *node = new(mem_ctx) call_node; + node->func = target; + this->current->callees.push_tail(node); + + /* Create a link from the callee to the caller. + */ + node = new(mem_ctx) call_node; + node->func = this->current; + target->callers.push_tail(node); + return visit_continue; + } + + function *current; + struct hash_table *function_hash; + void *mem_ctx; + bool progress; +}; + +static void +destroy_links(exec_list *list, function *f) +{ + foreach_list_safe(node, list) { + struct call_node *n = (struct call_node *) node; + + /* If this is the right function, remove it. Note that the loop cannot + * terminate now. There can be multiple links to a function if it is + * either called multiple times or calls multiple times. + */ + if (n->func == f) + n->remove(); + } +} + + +/** + * Remove a function if it has either no in or no out links + */ +static void +remove_unlinked_functions(const void *key, void *data, void *closure) +{ + has_recursion_visitor *visitor = (has_recursion_visitor *) closure; + function *f = (function *) data; + + if (f->callers.is_empty() || f->callees.is_empty()) { + while (!f->callers.is_empty()) { + struct call_node *n = (struct call_node *) f->callers.pop_head(); + destroy_links(& n->func->callees, f); + } + + while (!f->callees.is_empty()) { + struct call_node *n = (struct call_node *) f->callees.pop_head(); + destroy_links(& n->func->callers, f); + } + + hash_table_remove(visitor->function_hash, key); + visitor->progress = true; + } +} + + +static void +emit_errors_unlinked(const void *key, void *data, void *closure) +{ + struct _mesa_glsl_parse_state *state = + (struct _mesa_glsl_parse_state *) closure; + function *f = (function *) data; + YYLTYPE loc; + + char *proto = prototype_string(f->sig->return_type, + f->sig->function_name(), + &f->sig->parameters); + + memset(&loc, 0, sizeof(loc)); + _mesa_glsl_error(&loc, state, + "function `%s' has static recursion.", + proto); + ralloc_free(proto); +} + + +static void +emit_errors_linked(const void *key, void *data, void *closure) +{ + struct gl_shader_program *prog = + (struct gl_shader_program *) closure; + function *f = (function *) data; + + char *proto = prototype_string(f->sig->return_type, + f->sig->function_name(), + &f->sig->parameters); + + linker_error(prog, "function `%s' has static recursion.\n", proto); + ralloc_free(proto); + prog->LinkStatus = false; +} + + +void +detect_recursion_unlinked(struct _mesa_glsl_parse_state *state, + exec_list *instructions) +{ + has_recursion_visitor v; + + /* Collect all of the information about which functions call which other + * functions. + */ + v.run(instructions); + + /* Remove from the set all of the functions that either have no caller or + * call no other functions. Repeat until no functions are removed. + */ + do { + v.progress = false; + hash_table_call_foreach(v.function_hash, remove_unlinked_functions, & v); + } while (v.progress); + + + /* At this point any functions still in the hash must be part of a cycle. + */ + hash_table_call_foreach(v.function_hash, emit_errors_unlinked, state); +} + + +void +detect_recursion_linked(struct gl_shader_program *prog, + exec_list *instructions) +{ + has_recursion_visitor v; + + /* Collect all of the information about which functions call which other + * functions. + */ + v.run(instructions); + + /* Remove from the set all of the functions that either have no caller or + * call no other functions. Repeat until no functions are removed. + */ + do { + v.progress = false; + hash_table_call_foreach(v.function_hash, remove_unlinked_functions, & v); + } while (v.progress); + + + /* At this point any functions still in the hash must be part of a cycle. + */ + hash_table_call_foreach(v.function_hash, emit_errors_linked, prog); +} diff --git a/src/glsl/ir_hv_accept.cpp b/src/glsl/ir_hv_accept.cpp index 4a607dc8749..d33fc85bf0a 100644 --- a/src/glsl/ir_hv_accept.cpp +++ b/src/glsl/ir_hv_accept.cpp @@ -171,9 +171,11 @@ ir_texture::accept(ir_hierarchical_visitor *v) if (s != visit_continue) return (s == visit_continue_with_parent) ? visit_continue : s; - s = this->coordinate->accept(v); - if (s != visit_continue) - return (s == visit_continue_with_parent) ? visit_continue : s; + if (this->coordinate) { + s = this->coordinate->accept(v); + if (s != visit_continue) + return (s == visit_continue_with_parent) ? visit_continue : s; + } if (this->projector) { s = this->projector->accept(v); @@ -203,6 +205,7 @@ ir_texture::accept(ir_hierarchical_visitor *v) break; case ir_txl: case ir_txf: + case ir_txs: s = this->lod_info.lod->accept(v); if (s != visit_continue) return (s == visit_continue_with_parent) ? visit_continue : s; diff --git a/src/glsl/ir_optimization.h b/src/glsl/ir_optimization.h index 59a040751d9..f7808bdda9a 100644 --- a/src/glsl/ir_optimization.h +++ b/src/glsl/ir_optimization.h @@ -69,3 +69,7 @@ bool lower_variable_index_to_cond_assign(exec_list *instructions, bool lower_input, bool lower_output, bool lower_temp, bool lower_uniform); bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz); bool optimize_redundant_jumps(exec_list *instructions); + +ir_rvalue * +compare_index_block(exec_list *instructions, ir_variable *index, + unsigned base, unsigned components, void *mem_ctx); diff --git a/src/glsl/ir_print_visitor.cpp b/src/glsl/ir_print_visitor.cpp index 518910bd129..ea7858224bb 100644 --- a/src/glsl/ir_print_visitor.cpp +++ b/src/glsl/ir_print_visitor.cpp @@ -244,19 +244,21 @@ void ir_print_visitor::visit(ir_texture *ir) ir->sampler->accept(this); printf(" "); - ir->coordinate->accept(this); + if (ir->op != ir_txs) { + ir->coordinate->accept(this); - printf(" "); + printf(" "); - if (ir->offset != NULL) { - ir->offset->accept(this); - } else { - printf("0"); - } + if (ir->offset != NULL) { + ir->offset->accept(this); + } else { + printf("0"); + } - printf(" "); + printf(" "); + } - if (ir->op != ir_txf) { + if (ir->op != ir_txf && ir->op != ir_txs) { if (ir->projector) ir->projector->accept(this); else @@ -280,6 +282,7 @@ void ir_print_visitor::visit(ir_texture *ir) break; case ir_txl: case ir_txf: + case ir_txs: ir->lod_info.lod->accept(this); break; case ir_txd: diff --git a/src/glsl/ir_reader.cpp b/src/glsl/ir_reader.cpp index f3a621734ba..22009eebcb9 100644 --- a/src/glsl/ir_reader.cpp +++ b/src/glsl/ir_reader.cpp @@ -885,6 +885,8 @@ ir_reader::read_texture(s_expression *expr) { "tex", s_type, s_sampler, s_coord, s_offset, s_proj, s_shadow }; s_pattern txf_pattern[] = { "txf", s_type, s_sampler, s_coord, s_offset, s_lod }; + s_pattern txs_pattern[] = + { "txs", s_type, s_sampler, s_lod }; s_pattern other_pattern[] = { tag, s_type, s_sampler, s_coord, s_offset, s_proj, s_shadow, s_lod }; @@ -892,6 +894,8 @@ ir_reader::read_texture(s_expression *expr) op = ir_tex; } else if (MATCH(expr, txf_pattern)) { op = ir_txf; + } else if (MATCH(expr, txs_pattern)) { + op = ir_txs; } else if (MATCH(expr, other_pattern)) { op = ir_texture::get_opcode(tag->value()); if (op == -1) @@ -920,25 +924,27 @@ ir_reader::read_texture(s_expression *expr) } tex->set_sampler(sampler, type); - // Read coordinate (any rvalue) - tex->coordinate = read_rvalue(s_coord); - if (tex->coordinate == NULL) { - ir_read_error(NULL, "when reading coordinate in (%s ...)", - tex->opcode_string()); - return NULL; - } - - // Read texel offset - either 0 or an rvalue. - s_int *si_offset = SX_AS_INT(s_offset); - if (si_offset == NULL || si_offset->value() != 0) { - tex->offset = read_rvalue(s_offset); - if (tex->offset == NULL) { - ir_read_error(s_offset, "expected 0 or an expression"); + if (op != ir_txs) { + // Read coordinate (any rvalue) + tex->coordinate = read_rvalue(s_coord); + if (tex->coordinate == NULL) { + ir_read_error(NULL, "when reading coordinate in (%s ...)", + tex->opcode_string()); return NULL; } + + // Read texel offset - either 0 or an rvalue. + s_int *si_offset = SX_AS_INT(s_offset); + if (si_offset == NULL || si_offset->value() != 0) { + tex->offset = read_rvalue(s_offset); + if (tex->offset == NULL) { + ir_read_error(s_offset, "expected 0 or an expression"); + return NULL; + } + } } - if (op != ir_txf) { + if (op != ir_txf && op != ir_txs) { s_int *proj_as_int = SX_AS_INT(s_proj); if (proj_as_int && proj_as_int->value() == 1) { tex->projector = NULL; @@ -973,6 +979,7 @@ ir_reader::read_texture(s_expression *expr) break; case ir_txl: case ir_txf: + case ir_txs: tex->lod_info.lod = read_rvalue(s_lod); if (tex->lod_info.lod == NULL) { ir_read_error(NULL, "when reading LOD in (%s ...)", diff --git a/src/glsl/ir_rvalue_visitor.cpp b/src/glsl/ir_rvalue_visitor.cpp index ed6c7cb6a1a..193bcd2d7bd 100644 --- a/src/glsl/ir_rvalue_visitor.cpp +++ b/src/glsl/ir_rvalue_visitor.cpp @@ -63,6 +63,7 @@ ir_rvalue_visitor::visit_leave(ir_texture *ir) break; case ir_txf: case ir_txl: + case ir_txs: handle_rvalue(&ir->lod_info.lod); break; case ir_txd: diff --git a/src/glsl/ir_validate.cpp b/src/glsl/ir_validate.cpp index f3fceb2a57d..2d1c6097c57 100644 --- a/src/glsl/ir_validate.cpp +++ b/src/glsl/ir_validate.cpp @@ -59,7 +59,8 @@ public: virtual ir_visitor_status visit(ir_variable *v); virtual ir_visitor_status visit(ir_dereference_variable *ir); - virtual ir_visitor_status visit(ir_if *ir); + + virtual ir_visitor_status visit_enter(ir_if *ir); virtual ir_visitor_status visit_leave(ir_loop *ir); virtual ir_visitor_status visit_enter(ir_function *ir); @@ -102,7 +103,7 @@ ir_validate::visit(ir_dereference_variable *ir) } ir_visitor_status -ir_validate::visit(ir_if *ir) +ir_validate::visit_enter(ir_if *ir) { if (ir->condition->type != glsl_type::bool_type) { printf("ir_if condition %s type instead of bool.\n", @@ -541,7 +542,43 @@ ir_validate::visit_enter(ir_call *ir) abort(); } + const exec_node *formal_param_node = callee->parameters.head; + const exec_node *actual_param_node = ir->actual_parameters.head; + while (true) { + if (formal_param_node->is_tail_sentinel() + != actual_param_node->is_tail_sentinel()) { + printf("ir_call has the wrong number of parameters:\n"); + goto dump_ir; + } + if (formal_param_node->is_tail_sentinel()) { + break; + } + const ir_variable *formal_param + = (const ir_variable *) formal_param_node; + const ir_rvalue *actual_param + = (const ir_rvalue *) actual_param_node; + if (formal_param->type != actual_param->type) { + printf("ir_call parameter type mismatch:\n"); + goto dump_ir; + } + if (formal_param->mode == ir_var_out + || formal_param->mode == ir_var_inout) { + if (!actual_param->is_lvalue()) { + printf("ir_call out/inout parameters must be lvalues:\n"); + goto dump_ir; + } + } + formal_param_node = formal_param_node->next; + actual_param_node = actual_param_node->next; + } + return visit_continue; + +dump_ir: + ir->print(); + printf("callee:\n"); + callee->print(); + abort(); } void diff --git a/src/glsl/link_functions.cpp b/src/glsl/link_functions.cpp index 7ba760daa1a..acee3271249 100644 --- a/src/glsl/link_functions.cpp +++ b/src/glsl/link_functions.cpp @@ -91,8 +91,8 @@ public: if (sig == NULL) { /* FINISHME: Log the full signature of unresolved function. */ - linker_error_printf(this->prog, "unresolved reference to function " - "`%s'\n", name); + linker_error(this->prog, "unresolved reference to function `%s'\n", + name); this->success = false; return visit_stop; } @@ -104,10 +104,12 @@ public: if (f == NULL) { f = new(linked) ir_function(name); - /* Add the new function to the linked IR. + /* Add the new function to the linked IR. Put it at the end + * so that it comes after any global variable declarations + * that it refers to. */ linked->symbols->add_function(f); - linked->ir->push_head(f); + linked->ir->push_tail(f); } ir_function_signature *linked_sig = diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp index 34b64837a46..ba81c59ff2c 100644 --- a/src/glsl/linker.cpp +++ b/src/glsl/linker.cpp @@ -164,7 +164,7 @@ private: void -linker_error_printf(gl_shader_program *prog, const char *fmt, ...) +linker_error(gl_shader_program *prog, const char *fmt, ...) { va_list ap; @@ -172,6 +172,21 @@ linker_error_printf(gl_shader_program *prog, const char *fmt, ...) va_start(ap, fmt); ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); va_end(ap); + + prog->LinkStatus = false; +} + + +void +linker_warning(gl_shader_program *prog, const char *fmt, ...) +{ + va_list ap; + + ralloc_strcat(&prog->InfoLog, "error: "); + va_start(ap, fmt); + ralloc_vasprintf_append(&prog->InfoLog, fmt, ap); + va_end(ap); + } @@ -243,8 +258,7 @@ validate_vertex_shader_executable(struct gl_shader_program *prog, 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"); + linker_error(prog, "vertex shader does not write to `gl_Position'\n"); return false; } @@ -271,8 +285,8 @@ validate_fragment_shader_executable(struct gl_shader_program *prog, 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"); + linker_error(prog, "fragment shader writes to both " + "`gl_FragColor' and `gl_FragData'\n"); return false; } @@ -357,11 +371,11 @@ cross_validate_globals(struct gl_shader_program *prog, 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); + linker_error(prog, "%s `%s' declared as type " + "`%s' and type `%s'\n", + mode_string(var), + var->name, var->type->name, + existing->type->name); return false; } } @@ -369,9 +383,9 @@ cross_validate_globals(struct gl_shader_program *prog, 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); + linker_error(prog, "explicit locations for %s " + "`%s' have differing values\n", + mode_string(var), var->name); return false; } @@ -381,7 +395,7 @@ cross_validate_globals(struct gl_shader_program *prog, /* Validate layout qualifiers for gl_FragDepth. * - * From the AMD_conservative_depth spec: + * From the AMD/ARB_conservative_depth specs: * "If gl_FragDepth is redeclared in any fragment shader in * a program, it must be redeclared in all fragment shaders in that * program that have static assignments to gl_FragDepth. All @@ -392,12 +406,12 @@ cross_validate_globals(struct gl_shader_program *prog, 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, + linker_error(prog, "All redeclarations of gl_FragDepth in all fragment shaders " "in a single program must have the same set of qualifiers."); } if (var->used && layout_differs) { - linker_error_printf(prog, + linker_error(prog, "If gl_FragDepth is redeclared with a layout qualifier in" "any fragment shader, it must be redeclared with the same" "layout qualifier in all fragment shaders that have" @@ -410,9 +424,9 @@ cross_validate_globals(struct gl_shader_program *prog, 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); + linker_error(prog, "initializers for %s " + "`%s' have differing values\n", + mode_string(var), var->name); return false; } } else @@ -433,15 +447,15 @@ cross_validate_globals(struct gl_shader_program *prog, } if (existing->invariant != var->invariant) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching invariant qualifiers\n", - mode_string(var), var->name); + linker_error(prog, "declarations for %s `%s' have " + "mismatching invariant qualifiers\n", + mode_string(var), var->name); return false; } if (existing->centroid != var->centroid) { - linker_error_printf(prog, "declarations for %s `%s' have " - "mismatching centroid qualifiers\n", - mode_string(var), var->name); + linker_error(prog, "declarations for %s `%s' have " + "mismatching centroid qualifiers\n", + mode_string(var), var->name); return false; } } else @@ -529,13 +543,12 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, */ 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); + linker_error(prog, + "%s shader output `%s' declared as type `%s', " + "but %s shader input declared as type `%s'\n", + producer_stage, output->name, + output->type->name, + consumer_stage, input->type->name); return false; } } @@ -543,40 +556,40 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog, /* 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"); + linker_error(prog, + "%s shader output `%s' %s centroid qualifier, " + "but %s shader input %s centroid qualifier\n", + producer_stage, + output->name, + (output->centroid) ? "has" : "lacks", + consumer_stage, + (input->centroid) ? "has" : "lacks"); return false; } if (input->invariant != output->invariant) { - linker_error_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"); + linker_error(prog, + "%s shader output `%s' %s invariant qualifier, " + "but %s shader input %s invariant qualifier\n", + producer_stage, + output->name, + (output->invariant) ? "has" : "lacks", + consumer_stage, + (input->invariant) ? "has" : "lacks"); return false; } if (input->interpolation != output->interpolation) { - linker_error_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()); + linker_error(prog, + "%s shader output `%s' specifies %s " + "interpolation qualifier, " + "but %s shader input specifies %s " + "interpolation qualifier\n", + producer_stage, + output->name, + output->interpolation_string(), + consumer_stage, + input->interpolation_string()); return false; } } @@ -823,9 +836,8 @@ link_intrastage_shaders(void *mem_ctx, if ((other_sig != NULL) && other_sig->is_defined && !other_sig->is_builtin) { - linker_error_printf(prog, - "function `%s' is multiply defined", - f->name); + linker_error(prog, "function `%s' is multiply defined", + f->name); return NULL; } } @@ -849,9 +861,9 @@ link_intrastage_shaders(void *mem_ctx, } if (main == NULL) { - linker_error_printf(prog, "%s shader lacks `main'\n", - (shader_list[0]->Type == GL_VERTEX_SHADER) - ? "vertex" : "fragment"); + linker_error(prog, "%s shader lacks `main'\n", + (shader_list[0]->Type == GL_VERTEX_SHADER) + ? "vertex" : "fragment"); return NULL; } @@ -910,6 +922,14 @@ link_intrastage_shaders(void *mem_ctx, free(linking_shaders); +#ifdef DEBUG + /* At this point linked should contain all of the linked IR, so + * validate it to make sure nothing went wrong. + */ + if (linked) + validate_ir_tree(linked->ir); +#endif + /* Make a pass over all variable declarations to ensure that arrays with * unspecified sizes have a size specified. The size is inferred from the * max_array_access field. @@ -1309,10 +1329,10 @@ assign_attribute_or_color_locations(gl_shader_program *prog, * 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); + linker_error(prog, + "insufficient contiguous attribute locations " + "available for vertex shader input `%s'", + var->name); return false; } @@ -1343,7 +1363,7 @@ assign_attribute_or_color_locations(gl_shader_program *prog, foreach_list(node, sh->ir) { ir_variable *const var = ((ir_instruction *) node)->as_variable(); - if ((var == NULL) || (var->mode != direction)) + if ((var == NULL) || (var->mode != (unsigned) direction)) continue; if (var->explicit_location) { @@ -1353,11 +1373,10 @@ assign_attribute_or_color_locations(gl_shader_program *prog, if ((var->location >= (int)(max_index + generic_base)) || (var->location < 0)) { - linker_error_printf(prog, - "invalid explicit location %d specified for " - "`%s'\n", - (var->location < 0) ? var->location : attr, - var->name); + linker_error(prog, + "invalid explicit location %d specified for `%s'\n", + (var->location < 0) ? var->location : attr, + var->name); return false; } else if (var->location >= generic_base) { used_locations |= (use_mask << attr); @@ -1406,10 +1425,10 @@ assign_attribute_or_color_locations(gl_shader_program *prog, const char *const string = (target_index == MESA_SHADER_VERTEX) ? "vertex shader input" : "fragment shader output"; - linker_error_printf(prog, - "insufficient contiguous attribute locations " - "available for %s `%s'", - string, to_assign[i].var->name); + linker_error(prog, + "insufficient contiguous attribute locations " + "available for %s `%s'", + string, to_assign[i].var->name); return false; } @@ -1525,9 +1544,8 @@ assign_varying_locations(struct gl_context *ctx, * "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; + linker_error(prog, "fragment shader varying %s not written " + "by vertex shader\n.", var->name); } /* An 'in' variable is only really a shader input if its @@ -1544,17 +1562,17 @@ assign_varying_locations(struct gl_context *ctx, if (ctx->API == API_OPENGLES2 || prog->Version == 100) { if (varying_vectors > ctx->Const.MaxVarying) { - linker_error_printf(prog, "shader uses too many varying vectors " - "(%u > %u)\n", - varying_vectors, ctx->Const.MaxVarying); + linker_error(prog, "shader uses too many varying vectors " + "(%u > %u)\n", + varying_vectors, ctx->Const.MaxVarying); return false; } } else { const unsigned float_components = varying_vectors * 4; if (float_components > ctx->Const.MaxVarying * 4) { - linker_error_printf(prog, "shader uses too many varying components " - "(%u > %u)\n", - float_components, ctx->Const.MaxVarying * 4); + linker_error(prog, "shader uses too many varying components " + "(%u > %u)\n", + float_components, ctx->Const.MaxVarying * 4); return false; } } @@ -1618,8 +1636,8 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) 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"); + linker_error(prog, "all shaders must use same shading " + "language version\n"); goto done; } @@ -1702,6 +1720,10 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) if (prog->_LinkedShaders[i] == NULL) continue; + detect_recursion_linked(prog, prog->_LinkedShaders[i]->ir); + if (!prog->LinkStatus) + goto done; + while (do_common_optimization(prog->_LinkedShaders[i]->ir, true, 32)) ; } @@ -1716,12 +1738,10 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) * FINISHME: at least 16, so hardcode 16 for now. */ if (!assign_attribute_or_color_locations(prog, MESA_SHADER_VERTEX, 16)) { - prog->LinkStatus = false; goto done; } if (!assign_attribute_or_color_locations(prog, MESA_SHADER_FRAGMENT, ctx->Const.MaxDrawBuffers)) { - prog->LinkStatus = false; goto done; } @@ -1738,7 +1758,6 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) if (!assign_varying_locations(ctx, prog, prog->_LinkedShaders[prev], prog->_LinkedShaders[i])) { - prog->LinkStatus = false; goto done; } @@ -1770,11 +1789,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog) */ 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; + linker_error(prog, "program lacks a vertex shader\n"); } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) { - linker_error_printf(prog, "program lacks a fragment shader\n"); - prog->LinkStatus = false; + linker_error(prog, "program lacks a fragment shader\n"); } } diff --git a/src/glsl/linker.h b/src/glsl/linker.h index a8ce16a7ec1..769cf68b6ad 100644 --- a/src/glsl/linker.h +++ b/src/glsl/linker.h @@ -25,9 +25,6 @@ #ifndef GLSL_LINKER_H #define GLSL_LINKER_H -extern void -linker_error_printf(gl_shader_program *prog, const char *fmt, ...); - extern bool link_function_calls(gl_shader_program *prog, gl_shader *main, gl_shader **shader_list, unsigned num_shaders); diff --git a/src/glsl/lower_if_to_cond_assign.cpp b/src/glsl/lower_if_to_cond_assign.cpp index b637eb4fe1d..7b89a1539ce 100644 --- a/src/glsl/lower_if_to_cond_assign.cpp +++ b/src/glsl/lower_if_to_cond_assign.cpp @@ -47,6 +47,7 @@ #include "glsl_types.h" #include "ir.h" +#include "program/hash_table.h" class ir_if_to_cond_assign_visitor : public ir_hierarchical_visitor { public: @@ -55,6 +56,14 @@ public: this->progress = false; this->max_depth = max_depth; this->depth = 0; + + this->condition_variables = hash_table_ctor(0, hash_table_pointer_hash, + hash_table_pointer_compare); + } + + ~ir_if_to_cond_assign_visitor() + { + hash_table_dtor(this->condition_variables); } ir_visitor_status visit_enter(ir_if *); @@ -63,6 +72,8 @@ public: bool progress; unsigned max_depth; unsigned depth; + + struct hash_table *condition_variables; }; bool @@ -94,40 +105,43 @@ check_control_flow(ir_instruction *ir, void *data) void move_block_to_cond_assign(void *mem_ctx, - ir_if *if_ir, ir_variable *cond_var, bool then) + ir_if *if_ir, ir_rvalue *cond_expr, + exec_list *instructions, + struct hash_table *ht) { - exec_list *instructions; - - if (then) { - instructions = &if_ir->then_instructions; - } else { - instructions = &if_ir->else_instructions; - } - - foreach_iter(exec_list_iterator, iter, *instructions) { - ir_instruction *ir = (ir_instruction *)iter.get(); + foreach_list_safe(node, instructions) { + ir_instruction *ir = (ir_instruction *) node; if (ir->ir_type == ir_type_assignment) { ir_assignment *assign = (ir_assignment *)ir; - ir_rvalue *cond_expr; - ir_dereference *deref = new(mem_ctx) ir_dereference_variable(cond_var); - - if (then) { - cond_expr = deref; - } else { - cond_expr = new(mem_ctx) ir_expression(ir_unop_logic_not, - glsl_type::bool_type, - deref, - NULL); - } - if (!assign->condition) { - assign->condition = cond_expr; - } else { - assign->condition = new(mem_ctx) ir_expression(ir_binop_logic_and, - glsl_type::bool_type, - cond_expr, - assign->condition); + if (hash_table_find(ht, assign) == NULL) { + hash_table_insert(ht, assign, assign); + + /* If the LHS of the assignment is a condition variable that was + * previously added, insert an additional assignment of false to + * the variable. + */ + const bool assign_to_cv = + hash_table_find(ht, assign->lhs->variable_referenced()) != NULL; + + if (!assign->condition) { + if (assign_to_cv) { + assign->rhs = + new(mem_ctx) ir_expression(ir_binop_logic_and, + glsl_type::bool_type, + cond_expr->clone(mem_ctx, NULL), + assign->rhs); + } else { + assign->condition = cond_expr->clone(mem_ctx, NULL); + } + } else { + assign->condition = + new(mem_ctx) ir_expression(ir_binop_logic_and, + glsl_type::bool_type, + cond_expr->clone(mem_ctx, NULL), + assign->condition); + } } } @@ -142,6 +156,7 @@ ir_if_to_cond_assign_visitor::visit_enter(ir_if *ir) { (void) ir; this->depth++; + return visit_continue; } @@ -153,9 +168,7 @@ ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) return visit_continue; bool found_control_flow = false; - ir_variable *cond_var; ir_assignment *assign; - ir_dereference_variable *deref; /* Check that both blocks don't contain anything we can't support. */ foreach_iter(exec_list_iterator, then_iter, ir->then_instructions) { @@ -171,24 +184,62 @@ ir_if_to_cond_assign_visitor::visit_leave(ir_if *ir) void *mem_ctx = ralloc_parent(ir); - /* Store the condition to a variable so the assignment conditions are - * simpler. + /* Store the condition to a variable. Move all of the instructions from + * the then-clause of the if-statement. Use the condition variable as a + * condition for all assignments. */ - cond_var = new(mem_ctx) ir_variable(glsl_type::bool_type, - "if_to_cond_assign_condition", - ir_var_temporary); - ir->insert_before(cond_var); - - deref = new(mem_ctx) ir_dereference_variable(cond_var); - assign = new(mem_ctx) ir_assignment(deref, - ir->condition, NULL); + ir_variable *const then_var = + new(mem_ctx) ir_variable(glsl_type::bool_type, + "if_to_cond_assign_then", + ir_var_temporary); + ir->insert_before(then_var); + + ir_dereference_variable *then_cond = + new(mem_ctx) ir_dereference_variable(then_var); + + assign = new(mem_ctx) ir_assignment(then_cond, ir->condition); ir->insert_before(assign); - /* Now, move all of the instructions out of the if blocks, putting - * conditions on assignments. + move_block_to_cond_assign(mem_ctx, ir, then_cond, + &ir->then_instructions, + this->condition_variables); + + /* Add the new condition variable to the hash table. This allows us to + * find this variable when lowering other (enclosing) if-statements. + */ + hash_table_insert(this->condition_variables, then_var, then_var); + + /* If there are instructions in the else-clause, store the inverse of the + * condition to a variable. Move all of the instructions from the + * else-clause if the if-statement. Use the (inverse) condition variable + * as a condition for all assignments. */ - move_block_to_cond_assign(mem_ctx, ir, cond_var, true); - move_block_to_cond_assign(mem_ctx, ir, cond_var, false); + if (!ir->else_instructions.is_empty()) { + ir_variable *const else_var = + new(mem_ctx) ir_variable(glsl_type::bool_type, + "if_to_cond_assign_else", + ir_var_temporary); + ir->insert_before(else_var); + + ir_dereference_variable *else_cond = + new(mem_ctx) ir_dereference_variable(else_var); + + ir_rvalue *inverse = + new(mem_ctx) ir_expression(ir_unop_logic_not, + then_cond->clone(mem_ctx, NULL)); + + assign = new(mem_ctx) ir_assignment(else_cond, inverse); + ir->insert_before(assign); + + move_block_to_cond_assign(mem_ctx, ir, else_cond, + &ir->else_instructions, + this->condition_variables); + + /* Add the new condition variable to the hash table. This allows us to + * find this variable when lowering other (enclosing) if-statements. + */ + hash_table_insert(this->condition_variables, else_var, else_var); + } ir->remove(); diff --git a/src/glsl/lower_instructions.cpp b/src/glsl/lower_instructions.cpp index 806f8639959..23aa19bde6f 100644 --- a/src/glsl/lower_instructions.cpp +++ b/src/glsl/lower_instructions.cpp @@ -166,6 +166,10 @@ lower_instructions_visitor::div_to_mul_rcp(ir_expression *ir) else op0 = new(ir) ir_expression(ir_unop_u2f, vec_type, ir->operands[0], NULL); + vec_type = glsl_type::get_instance(GLSL_TYPE_FLOAT, + ir->type->vector_elements, + ir->type->matrix_columns); + op0 = new(ir) ir_expression(ir_binop_mul, vec_type, op0, op1); if (ir->operands[1]->type->base_type == GLSL_TYPE_INT) { diff --git a/src/glsl/lower_variable_index_to_cond_assign.cpp b/src/glsl/lower_variable_index_to_cond_assign.cpp index 8eb1612f0a0..f8e4a1de428 100644 --- a/src/glsl/lower_variable_index_to_cond_assign.cpp +++ b/src/glsl/lower_variable_index_to_cond_assign.cpp @@ -29,6 +29,21 @@ * * Pre-DX10 GPUs often don't have a native way to do this operation, * and this works around that. + * + * The lowering process proceeds as follows. Each non-constant index + * found in an r-value is converted to a canonical form \c array[i]. Each + * element of the array is conditionally assigned to a temporary by comparing + * \c i to a constant index. This is done by cloning the canonical form and + * replacing all occurances of \c i with a constant. Each remaining occurance + * of the canonical form in the IR is replaced with a dereference of the + * temporary variable. + * + * L-values with non-constant indices are handled similarly. In this case, + * the RHS of the assignment is assigned to a temporary. The non-constant + * index is replace with the canonical form (just like for r-values). The + * temporary is conditionally assigned to each element of the canonical form + * by comparing \c i with each index. The same clone-and-replace scheme is + * used. */ #include "ir.h" @@ -37,10 +52,140 @@ #include "glsl_types.h" #include "main/macros.h" +/** + * Generate a comparison value for a block of indices + * + * Lowering passes for non-constant indexing of arrays, matrices, or vectors + * can use this to generate blocks of index comparison values. + * + * \param instructions List where new instructions will be appended + * \param index \c ir_variable containing the desired index + * \param base Base value for this block of comparisons + * \param components Number of unique index values to compare. This must + * be on the range [1, 4]. + * \param mem_ctx ralloc memory context to be used for all allocations. + * + * \returns + * An \c ir_rvalue that \b must be cloned for each use in conditional + * assignments, etc. + */ +ir_rvalue * +compare_index_block(exec_list *instructions, ir_variable *index, + unsigned base, unsigned components, void *mem_ctx) +{ + ir_rvalue *broadcast_index = new(mem_ctx) ir_dereference_variable(index); + + assert(index->type->is_scalar()); + assert(index->type->base_type == GLSL_TYPE_INT); + assert(components >= 1 && components <= 4); + + if (components > 1) { + const ir_swizzle_mask m = { 0, 0, 0, 0, components, false }; + broadcast_index = new(mem_ctx) ir_swizzle(broadcast_index, m); + } + + /* Compare the desired index value with the next block of four indices. + */ + ir_constant_data test_indices_data; + memset(&test_indices_data, 0, sizeof(test_indices_data)); + test_indices_data.i[0] = base; + test_indices_data.i[1] = base + 1; + test_indices_data.i[2] = base + 2; + test_indices_data.i[3] = base + 3; + + ir_constant *const test_indices = + new(mem_ctx) ir_constant(broadcast_index->type, + &test_indices_data); + + ir_rvalue *const condition_val = + new(mem_ctx) ir_expression(ir_binop_equal, + &glsl_type::bool_type[components - 1], + broadcast_index, + test_indices); + + ir_variable *const condition = + new(mem_ctx) ir_variable(condition_val->type, + "dereference_condition", + ir_var_temporary); + instructions->push_tail(condition); + + ir_rvalue *const cond_deref = + new(mem_ctx) ir_dereference_variable(condition); + instructions->push_tail(new(mem_ctx) ir_assignment(cond_deref, condition_val, 0)); + + return cond_deref; +} + +static inline bool +is_array_or_matrix(const ir_instruction *ir) +{ + return (ir->type->is_array() || ir->type->is_matrix()); +} + +/** + * Replace a dereference of a variable with a specified r-value + * + * Each time a dereference of the specified value is replaced, the r-value + * tree is cloned. + */ +class deref_replacer : public ir_rvalue_visitor { +public: + deref_replacer(const ir_variable *variable_to_replace, ir_rvalue *value) + : variable_to_replace(variable_to_replace), value(value), + progress(false) + { + assert(this->variable_to_replace != NULL); + assert(this->value != NULL); + } + + virtual void handle_rvalue(ir_rvalue **rvalue) + { + ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable(); + + if ((dv != NULL) && (dv->var == this->variable_to_replace)) { + this->progress = true; + *rvalue = this->value->clone(ralloc_parent(*rvalue), NULL); + } + } + + const ir_variable *variable_to_replace; + ir_rvalue *value; + bool progress; +}; + +/** + * Find a variable index dereference of an array in an rvalue tree + */ +class find_variable_index : public ir_hierarchical_visitor { +public: + find_variable_index() + : deref(NULL) + { + /* empty */ + } + + virtual ir_visitor_status visit_enter(ir_dereference_array *ir) + { + if (is_array_or_matrix(ir->array) + && (ir->array_index->as_constant() == NULL)) { + this->deref = ir; + return visit_stop; + } + + return visit_continue; + } + + /** + * First array dereference found in the tree that has a non-constant index. + */ + ir_dereference_array *deref; +}; + struct assignment_generator { ir_instruction* base_ir; - ir_rvalue* array; + ir_dereference *rvalue; + ir_variable *old_index; bool is_write; unsigned int write_mask; ir_variable* var; @@ -55,18 +200,23 @@ struct assignment_generator * underlying variable. */ void *mem_ctx = ralloc_parent(base_ir); - ir_dereference *element = - new(mem_ctx) ir_dereference_array(this->array->clone(mem_ctx, NULL), - new(mem_ctx) ir_constant(i)); - ir_rvalue *variable = new(mem_ctx) ir_dereference_variable(this->var); - ir_assignment *assignment; - if (is_write) { - assignment = new(mem_ctx) ir_assignment(element, variable, condition, - write_mask); - } else { - assignment = new(mem_ctx) ir_assignment(variable, element, condition); - } + /* Clone the old r-value in its entirety. Then replace any occurances of + * the old variable index with the new constant index. + */ + ir_dereference *element = this->rvalue->clone(mem_ctx, NULL); + ir_constant *const index = new(mem_ctx) ir_constant(i); + deref_replacer r(this->old_index, index); + element->accept(&r); + assert(r.progress); + + /* Generate a conditional assignment to (or from) the constant indexed + * array dereference. + */ + ir_rvalue *variable = new(mem_ctx) ir_dereference_variable(this->var); + ir_assignment *const assignment = (is_write) + ? new(mem_ctx) ir_assignment(element, variable, condition, write_mask) + : new(mem_ctx) ir_assignment(variable, element, condition); list->push_tail(assignment); } @@ -118,54 +268,17 @@ struct switch_generator for (unsigned i = first; i < end; i += 4) { const unsigned comps = MIN2(condition_components, end - i); - ir_rvalue *broadcast_index = - new(this->mem_ctx) ir_dereference_variable(index); - - if (comps) { - const ir_swizzle_mask m = { 0, 0, 0, 0, comps, false }; - broadcast_index = new(this->mem_ctx) ir_swizzle(broadcast_index, m); - } - - /* Compare the desired index value with the next block of four indices. - */ - ir_constant_data test_indices_data; - memset(&test_indices_data, 0, sizeof(test_indices_data)); - test_indices_data.i[0] = i; - test_indices_data.i[1] = i + 1; - test_indices_data.i[2] = i + 2; - test_indices_data.i[3] = i + 3; - ir_constant *const test_indices = - new(this->mem_ctx) ir_constant(broadcast_index->type, - &test_indices_data); - - ir_rvalue *const condition_val = - new(this->mem_ctx) ir_expression(ir_binop_equal, - &glsl_type::bool_type[comps - 1], - broadcast_index, - test_indices); - - ir_variable *const condition = - new(this->mem_ctx) ir_variable(condition_val->type, - "dereference_array_condition", - ir_var_temporary); - list->push_tail(condition); - ir_rvalue *const cond_deref = - new(this->mem_ctx) ir_dereference_variable(condition); - list->push_tail(new(this->mem_ctx) ir_assignment(cond_deref, - condition_val, 0)); + compare_index_block(list, index, i, comps, this->mem_ctx); if (comps == 1) { - ir_rvalue *const cond_deref = - new(this->mem_ctx) ir_dereference_variable(condition); - - this->generator.generate(i, cond_deref, list); + this->generator.generate(i, cond_deref->clone(this->mem_ctx, NULL), + list); } else { for (unsigned j = 0; j < comps; j++) { - ir_rvalue *const cond_deref = - new(this->mem_ctx) ir_dereference_variable(condition); ir_rvalue *const cond_swiz = - new(this->mem_ctx) ir_swizzle(cond_deref, j, 0, 0, 0, 1); + new(this->mem_ctx) ir_swizzle(cond_deref->clone(this->mem_ctx, NULL), + j, 0, 0, 0, 1); this->generator.generate(i + j, cond_swiz, list); } @@ -233,21 +346,18 @@ public: bool lower_temps; bool lower_uniforms; - bool is_array_or_matrix(const ir_instruction *ir) const - { - return (ir->type->is_array() || ir->type->is_matrix()); - } - - bool needs_lowering(ir_dereference_array *deref) const + bool storage_type_needs_lowering(ir_dereference_array *deref) const { - if (deref == NULL || deref->array_index->as_constant() - || !is_array_or_matrix(deref->array)) - return false; - - if (deref->array->ir_type == ir_type_constant) + /* If a variable isn't eventually the target of this dereference, then + * it must be a constant or some sort of anonymous temporary storage. + * + * FINISHME: Is this correct? Most drivers treat arrays of constants as + * FINISHME: uniforms. It seems like this should do the same. + */ + const ir_variable *const var = deref->array->variable_referenced(); + if (var == NULL) return this->lower_temps; - const ir_variable *const var = deref->array->variable_referenced(); switch (var->mode) { case ir_var_auto: case ir_var_temporary: @@ -267,8 +377,18 @@ public: return false; } + bool needs_lowering(ir_dereference_array *deref) const + { + if (deref == NULL || deref->array_index->as_constant() + || !is_array_or_matrix(deref->array)) + return false; + + return this->storage_type_needs_lowering(deref); + } + ir_variable *convert_dereference_array(ir_dereference_array *orig_deref, - ir_assignment* orig_assign) + ir_assignment* orig_assign, + ir_dereference *orig_base) { assert(is_array_or_matrix(orig_deref->array)); @@ -314,9 +434,12 @@ public: new(mem_ctx) ir_assignment(lhs, orig_deref->array_index, NULL); base_ir->insert_before(assign); + orig_deref->array_index = lhs->clone(mem_ctx, NULL); + assignment_generator ag; - ag.array = orig_deref->array; + ag.rvalue = orig_base; ag.base_ir = base_ir; + ag.old_index = index; ag.var = var; if (orig_assign) { ag.is_write = true; @@ -327,21 +450,40 @@ public: switch_generator sg(ag, index, 4, 4); - exec_list list; - sg.generate(0, length, &list); - base_ir->insert_before(&list); + /* If the original assignment has a condition, respect that original + * condition! This is acomplished by wrapping the new conditional + * assignments in an if-statement that uses the original condition. + */ + if ((orig_assign != NULL) && (orig_assign->condition != NULL)) { + /* No need to clone the condition because the IR that it hangs on is + * going to be removed from the instruction sequence. + */ + ir_if *if_stmt = new(mem_ctx) ir_if(orig_assign->condition); + + sg.generate(0, length, &if_stmt->then_instructions); + base_ir->insert_before(if_stmt); + } else { + exec_list list; + + sg.generate(0, length, &list); + base_ir->insert_before(&list); + } return var; } virtual void handle_rvalue(ir_rvalue **pir) { + if (this->in_assignee) + return; + if (!*pir) return; ir_dereference_array* orig_deref = (*pir)->as_dereference_array(); if (needs_lowering(orig_deref)) { - ir_variable* var = convert_dereference_array(orig_deref, 0); + ir_variable *var = + convert_dereference_array(orig_deref, NULL, orig_deref); assert(var); *pir = new(ralloc_parent(base_ir)) ir_dereference_variable(var); this->progress = true; @@ -353,10 +495,11 @@ public: { ir_rvalue_visitor::visit_leave(ir); - ir_dereference_array *orig_deref = ir->lhs->as_dereference_array(); + find_variable_index f; + ir->lhs->accept(&f); - if (needs_lowering(orig_deref)) { - convert_dereference_array(orig_deref, ir); + if ((f.deref != NULL) && storage_type_needs_lowering(f.deref)) { + convert_dereference_array(f.deref, ir, ir->lhs); ir->remove(); this->progress = true; } @@ -377,7 +520,17 @@ lower_variable_index_to_cond_assign(exec_list *instructions, lower_temp, lower_uniform); - visit_list_elements(&v, instructions); - - return v.progress; + /* Continue lowering until no progress is made. If there are multiple + * levels of indirection (e.g., non-constant indexing of array elements and + * matrix columns of an array of matrix), each pass will only lower one + * level of indirection. + */ + bool progress_ever = false; + do { + v.progress = false; + visit_list_elements(&v, instructions); + progress_ever = v.progress || progress_ever; + } while (v.progress); + + return progress_ever; } diff --git a/src/glsl/lower_vec_index_to_cond_assign.cpp b/src/glsl/lower_vec_index_to_cond_assign.cpp index 3c4d93201d2..fce9c3424a1 100644 --- a/src/glsl/lower_vec_index_to_cond_assign.cpp +++ b/src/glsl/lower_vec_index_to_cond_assign.cpp @@ -71,8 +71,6 @@ ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue ir_assignment *assign; ir_variable *index, *var; ir_dereference *deref; - ir_expression *condition; - ir_swizzle *swizzle; int i; if (!orig_deref) @@ -86,39 +84,52 @@ ir_vec_index_to_cond_assign_visitor::convert_vec_index_to_cond_assign(ir_rvalue assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); + exec_list list; + /* Store the index to a temporary to avoid reusing its tree. */ index = new(base_ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i", ir_var_temporary); - base_ir->insert_before(index); + list.push_tail(index); deref = new(base_ir) ir_dereference_variable(index); assign = new(base_ir) ir_assignment(deref, orig_deref->array_index, NULL); - base_ir->insert_before(assign); + list.push_tail(assign); /* Temporary where we store whichever value we swizzle out. */ var = new(base_ir) ir_variable(ir->type, "vec_index_tmp_v", ir_var_temporary); - base_ir->insert_before(var); + list.push_tail(var); + + /* Generate a single comparison condition "mask" for all of the components + * in the vector. + */ + ir_rvalue *const cond_deref = + compare_index_block(&list, index, 0, + orig_deref->array->type->vector_elements, + mem_ctx); /* Generate a conditional move of each vector element to the temp. */ for (i = 0; i < orig_deref->array->type->vector_elements; i++) { - deref = new(base_ir) ir_dereference_variable(index); - condition = new(base_ir) ir_expression(ir_binop_equal, - glsl_type::bool_type, - deref, - new(base_ir) ir_constant(i)); + ir_rvalue *condition_swizzle = + new(base_ir) ir_swizzle(cond_deref->clone(ir, NULL), i, 0, 0, 0, 1); /* Just clone the rest of the deref chain when trying to get at the * underlying variable. */ - swizzle = new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), - i, 0, 0, 0, 1); + ir_rvalue *swizzle = + new(base_ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), + i, 0, 0, 0, 1); deref = new(base_ir) ir_dereference_variable(var); - assign = new(base_ir) ir_assignment(deref, swizzle, condition); - base_ir->insert_before(assign); + assign = new(base_ir) ir_assignment(deref, swizzle, condition_swizzle); + list.push_tail(assign); } + /* Put all of the new instructions in the IR stream before the old + * instruction. + */ + base_ir->insert_before(&list); + this->progress = true; return new(base_ir) ir_dereference_variable(var); } @@ -171,42 +182,66 @@ ir_vec_index_to_cond_assign_visitor::visit_leave(ir_assignment *ir) assert(orig_deref->array_index->type->base_type == GLSL_TYPE_INT); + exec_list list; + /* Store the index to a temporary to avoid reusing its tree. */ index = new(ir) ir_variable(glsl_type::int_type, "vec_index_tmp_i", ir_var_temporary); - ir->insert_before(index); + list.push_tail(index); deref = new(ir) ir_dereference_variable(index); assign = new(ir) ir_assignment(deref, orig_deref->array_index, NULL); - ir->insert_before(assign); + list.push_tail(assign); /* Store the RHS to a temporary to avoid reusing its tree. */ var = new(ir) ir_variable(ir->rhs->type, "vec_index_tmp_v", ir_var_temporary); - ir->insert_before(var); + list.push_tail(var); deref = new(ir) ir_dereference_variable(var); assign = new(ir) ir_assignment(deref, ir->rhs, NULL); - ir->insert_before(assign); + list.push_tail(assign); + + /* Generate a single comparison condition "mask" for all of the components + * in the vector. + */ + ir_rvalue *const cond_deref = + compare_index_block(&list, index, 0, + orig_deref->array->type->vector_elements, + mem_ctx); /* Generate a conditional move of each vector element to the temp. */ for (i = 0; i < orig_deref->array->type->vector_elements; i++) { - ir_rvalue *condition, *swizzle; + ir_rvalue *condition_swizzle = + new(ir) ir_swizzle(cond_deref->clone(ir, NULL), i, 0, 0, 0, 1); - deref = new(ir) ir_dereference_variable(index); - condition = new(ir) ir_expression(ir_binop_equal, - glsl_type::bool_type, - deref, - new(ir) ir_constant(i)); /* Just clone the rest of the deref chain when trying to get at the * underlying variable. */ - swizzle = new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), - i, 0, 0, 0, 1); + ir_rvalue *swizzle = + new(ir) ir_swizzle(orig_deref->array->clone(mem_ctx, NULL), + i, 0, 0, 0, 1); deref = new(ir) ir_dereference_variable(var); - assign = new(ir) ir_assignment(swizzle, deref, condition); - ir->insert_before(assign); + assign = new(ir) ir_assignment(swizzle, deref, condition_swizzle); + list.push_tail(assign); } + + /* If the original assignment has a condition, respect that original + * condition! This is acomplished by wrapping the new conditional + * assignments in an if-statement that uses the original condition. + */ + if (ir->condition != NULL) { + /* No need to clone the condition because the IR that it hangs on is + * going to be removed from the instruction sequence. + */ + ir_if *if_stmt = new(mem_ctx) ir_if(ir->condition); + + list.move_nodes_to(&if_stmt->then_instructions); + ir->insert_before(if_stmt); + } else { + ir->insert_before(&list); + } + ir->remove(); this->progress = true; diff --git a/src/glsl/main.cpp b/src/glsl/main.cpp index 7952bb1a3e3..01921375070 100644 --- a/src/glsl/main.cpp +++ b/src/glsl/main.cpp @@ -24,85 +24,30 @@ #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; -} +#include "standalone_scaffolding.h" 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; - ctx->Extensions.EXT_texture3D = GL_TRUE; + initialize_context_to_defaults(ctx, api); /* GLSL 1.30 isn't fully supported, but we need to advertise 1.30 so that * the built-in functions for 1.30 can be built. */ ctx->Const.GLSLVersion = 130; - /* 1.10 minimums. */ - ctx->Const.MaxLights = 8; ctx->Const.MaxClipPlanes = 8; - ctx->Const.MaxTextureUnits = 2; + ctx->Const.MaxDrawBuffers = 2; /* More than the 1.10 minimum to appease parser tests taken from * apps that (hopefully) already checked the number of coords. */ ctx->Const.MaxTextureCoordUnits = 4; - ctx->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; } @@ -275,6 +220,7 @@ main(int argc, char **argv) whole_program = rzalloc (NULL, struct gl_shader_program); assert(whole_program != NULL); + whole_program->InfoLog = ralloc_strdup(whole_program, ""); for (/* empty */; argc > optind; optind++) { whole_program->Shaders = diff --git a/src/glsl/opt_dead_functions.cpp b/src/glsl/opt_dead_functions.cpp index 7c64c618c0c..51c77e3b947 100644 --- a/src/glsl/opt_dead_functions.cpp +++ b/src/glsl/opt_dead_functions.cpp @@ -50,7 +50,6 @@ public: ir_dead_functions_visitor() { this->mem_ctx = ralloc_context(NULL); - this->seen_another_function_signature = false; } ~ir_dead_functions_visitor() @@ -65,8 +64,6 @@ public: bool (*predicate)(ir_instruction *ir); - bool seen_another_function_signature; - /* List of signature_entry */ exec_list signature_list; void *mem_ctx; @@ -97,13 +94,7 @@ ir_dead_functions_visitor::visit_enter(ir_function_signature *ir) entry->used = true; } - /* If this is the first signature to look at, no need to descend to see - * if it has calls to another function signature. - */ - if (!this->seen_another_function_signature) { - this->seen_another_function_signature = true; - return visit_continue_with_parent; - } + return visit_continue; } diff --git a/src/glsl/opt_tree_grafting.cpp b/src/glsl/opt_tree_grafting.cpp index 1ef940f9c72..22a1749b9dd 100644 --- a/src/glsl/opt_tree_grafting.cpp +++ b/src/glsl/opt_tree_grafting.cpp @@ -258,6 +258,7 @@ ir_tree_grafting_visitor::visit_enter(ir_texture *ir) break; case ir_txf: case ir_txl: + case ir_txs: if (do_graft(&ir->lod_info.lod)) return visit_stop; break; diff --git a/src/glsl/program.h b/src/glsl/program.h index db602fa9ec2..437ca1462fa 100644 --- a/src/glsl/program.h +++ b/src/glsl/program.h @@ -25,3 +25,11 @@ extern void link_shaders(struct gl_context *ctx, struct gl_shader_program *prog); + +extern void +linker_error(gl_shader_program *prog, const char *fmt, ...) + PRINTFLIKE(2, 3); + +extern void +linker_warning(gl_shader_program *prog, const char *fmt, ...) + PRINTFLIKE(2, 3); diff --git a/src/glsl/ralloc.c b/src/glsl/ralloc.c index 6a5eac6b9a3..fb48a91c564 100644 --- a/src/glsl/ralloc.c +++ b/src/glsl/ralloc.c @@ -28,6 +28,11 @@ #include <string.h> #include <stdint.h> +/* Android defines SIZE_MAX in limits.h, instead of the standard stdint.h */ +#ifdef ANDROID +#include <limits.h> +#endif + #include "ralloc.h" #ifdef __GNUC__ diff --git a/src/glsl/s_expression.cpp b/src/glsl/s_expression.cpp index a922a50d3b9..e704a3be20d 100644 --- a/src/glsl/s_expression.cpp +++ b/src/glsl/s_expression.cpp @@ -25,10 +25,13 @@ #include <assert.h> #include "s_expression.h" -s_symbol::s_symbol(const char *tmp, size_t n) +s_symbol::s_symbol(const char *str, size_t n) { - this->str = ralloc_strndup (this, tmp, n); - assert(this->str != NULL); + /* Assume the given string is already nul-terminated and in memory that + * will live as long as this node. + */ + assert(str[n] == '\0'); + this->str = str; } s_list::s_list() @@ -36,22 +39,26 @@ s_list::s_list() } static void -skip_whitespace(const char *& src) +skip_whitespace(const char *&src, char *&symbol_buffer) { - src += strspn(src, " \v\t\r\n"); + size_t n = strspn(src, " \v\t\r\n"); + src += n; + symbol_buffer += n; /* Also skip Scheme-style comments: semi-colon 'til end of line */ if (src[0] == ';') { - src += strcspn(src, "\n"); - skip_whitespace(src); + n = strcspn(src, "\n"); + src += n; + symbol_buffer += n; + skip_whitespace(src, symbol_buffer); } } static s_expression * -read_atom(void *ctx, const char *& src) +read_atom(void *ctx, const char *&src, char *&symbol_buffer) { s_expression *expr = NULL; - skip_whitespace(src); + skip_whitespace(src, symbol_buffer); size_t n = strcspn(src, "( \v\t\r\n);"); if (n == 0) @@ -70,44 +77,65 @@ read_atom(void *ctx, const char *& src) expr = new(ctx) s_int(i); } else { // Not a number; return a symbol. - expr = new(ctx) s_symbol(src, n); + symbol_buffer[n] = '\0'; + expr = new(ctx) s_symbol(symbol_buffer, n); } src += n; + symbol_buffer += n; return expr; } -s_expression * -s_expression::read_expression(void *ctx, const char *&src) +static s_expression * +__read_expression(void *ctx, const char *&src, char *&symbol_buffer) { - assert(src != NULL); - - s_expression *atom = read_atom(ctx, src); + s_expression *atom = read_atom(ctx, src, symbol_buffer); if (atom != NULL) return atom; - skip_whitespace(src); + skip_whitespace(src, symbol_buffer); if (src[0] == '(') { ++src; + ++symbol_buffer; s_list *list = new(ctx) s_list; s_expression *expr; - while ((expr = read_expression(ctx, src)) != NULL) { + while ((expr = __read_expression(ctx, src, symbol_buffer)) != NULL) { list->subexpressions.push_tail(expr); } - skip_whitespace(src); + skip_whitespace(src, symbol_buffer); if (src[0] != ')') { printf("Unclosed expression (check your parenthesis).\n"); return NULL; } ++src; + ++symbol_buffer; return list; } return NULL; } +s_expression * +s_expression::read_expression(void *ctx, const char *&src) +{ + assert(src != NULL); + + /* When we encounter a Symbol, we need to save a nul-terminated copy of + * the string. However, ralloc_strndup'ing every individual Symbol is + * extremely expensive. We could avoid this by simply overwriting the + * next character (guaranteed to be whitespace, parens, or semicolon) with + * a nul-byte. But overwriting non-whitespace would mess up parsing. + * + * So, just copy the whole buffer ahead of time. Walk both, leaving the + * original source string unmodified, and altering the copy to contain the + * necessary nul-bytes whenever we encounter a symbol. + */ + char *symbol_buffer = ralloc_strdup(ctx, src); + return __read_expression(ctx, src, symbol_buffer); +} + void s_int::print() { printf("%d", this->val); diff --git a/src/glsl/s_expression.h b/src/glsl/s_expression.h index c9dc676b319..642af19b439 100644 --- a/src/glsl/s_expression.h +++ b/src/glsl/s_expression.h @@ -129,7 +129,7 @@ public: void print(); private: - char *str; + const char *str; }; /* Lists of expressions: (expr1 ... exprN) */ diff --git a/src/glsl/standalone_scaffolding.cpp b/src/glsl/standalone_scaffolding.cpp new file mode 100644 index 00000000000..bbd7bb91310 --- /dev/null +++ b/src/glsl/standalone_scaffolding.cpp @@ -0,0 +1,93 @@ +/* + * Copyright © 2011 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. + */ + +/* This file declares stripped-down versions of functions that + * normally exist outside of the glsl folder, so that they can be used + * when running the GLSL compiler standalone (for unit testing or + * compiling builtins). + */ + +#include "standalone_scaffolding.h" + +#include <assert.h> +#include <string.h> +#include "ralloc.h" + +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; +} + +void initialize_context_to_defaults(struct gl_context *ctx, gl_api api) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->API = api; + + ctx->Extensions.dummy_false = false; + ctx->Extensions.dummy_true = true; + ctx->Extensions.ARB_ES2_compatibility = true; + ctx->Extensions.ARB_draw_buffers = true; + ctx->Extensions.ARB_draw_instanced = true; + ctx->Extensions.ARB_fragment_coord_conventions = true; + ctx->Extensions.EXT_texture_array = true; + ctx->Extensions.NV_texture_rectangle = true; + ctx->Extensions.EXT_texture3D = true; + + ctx->Const.GLSLVersion = 120; + + /* 1.20 minimums. */ + ctx->Const.MaxLights = 8; + ctx->Const.MaxClipPlanes = 6; + ctx->Const.MaxTextureUnits = 2; + ctx->Const.MaxTextureCoordUnits = 2; + ctx->Const.VertexProgram.MaxAttribs = 16; + + ctx->Const.VertexProgram.MaxUniformComponents = 512; + ctx->Const.MaxVarying = 8; /* == gl_MaxVaryingFloats / 4 */ + ctx->Const.MaxVertexTextureImageUnits = 0; + ctx->Const.MaxCombinedTextureImageUnits = 2; + ctx->Const.MaxTextureImageUnits = 2; + ctx->Const.FragmentProgram.MaxUniformComponents = 64; + + ctx->Const.MaxDrawBuffers = 1; +} diff --git a/src/glsl/standalone_scaffolding.h b/src/glsl/standalone_scaffolding.h new file mode 100644 index 00000000000..87733200670 --- /dev/null +++ b/src/glsl/standalone_scaffolding.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2011 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. + */ + +/* This file declares stripped-down versions of functions that + * normally exist outside of the glsl folder, so that they can be used + * when running the GLSL compiler standalone (for unit testing or + * compiling builtins). + */ + +#pragma once +#ifndef STANDALONE_SCAFFOLDING_H +#define STANDALONE_SCAFFOLDING_H + +#include "main/mtypes.h" + +extern "C" void +_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr, + struct gl_shader *sh); + +extern "C" struct gl_shader * +_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type); + +/** + * Initialize the given gl_context structure to a reasonable set of + * defaults representing the minimum capabilities required by the + * OpenGL spec. + * + * This is used when compiling builtin functions and in testing, when + * we don't have a connection to an actual driver. + */ +void initialize_context_to_defaults(struct gl_context *ctx, gl_api api); + + +#endif /* STANDALONE_SCAFFOLDING_H */ diff --git a/src/glsl/test.cpp b/src/glsl/test.cpp new file mode 100644 index 00000000000..b1ff92ed1d4 --- /dev/null +++ b/src/glsl/test.cpp @@ -0,0 +1,78 @@ +/* + * Copyright © 2011 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 test.cpp + * + * Standalone tests for the GLSL compiler. + * + * This file provides a standalone executable which can be used to + * test components of the GLSL. + * + * Each test is a function with the same signature as main(). The + * main function interprets its first argument as the name of the test + * to run, strips out that argument, and then calls the test function. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "test_optpass.h" + +/** + * Print proper usage and exit with failure. + */ +static void +usage_fail(const char *name) +{ + printf("*** usage: %s <command> <options>\n", name); + printf("\n"); + printf("Possible commands are:\n"); + printf(" optpass: test an optimization pass in isolation\n"); + exit(EXIT_FAILURE); +} + +static const char *extract_command_from_argv(int *argc, char **argv) +{ + if (*argc < 2) { + usage_fail(argv[0]); + } + const char *command = argv[1]; + --*argc; + memmove(&argv[1], &argv[2], (*argc) * sizeof(argv[1])); + return command; +} + +int main(int argc, char **argv) +{ + const char *command = extract_command_from_argv(&argc, argv); + if (strcmp(command, "optpass") == 0) { + return test_optpass(argc, argv); + } else { + usage_fail(argv[0]); + } + + /* Execution should never reach here. */ + return EXIT_FAILURE; +} diff --git a/src/glsl/test_optpass.cpp b/src/glsl/test_optpass.cpp new file mode 100644 index 00000000000..89b7f8338dc --- /dev/null +++ b/src/glsl/test_optpass.cpp @@ -0,0 +1,273 @@ +/* + * Copyright © 2011 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 test_optpass.cpp + * + * Standalone test for optimization passes. + * + * This file provides the "optpass" command for the standalone + * glsl_test app. It accepts either GLSL or high-level IR as input, + * and performs the optimiation passes specified on the command line. + * It outputs the IR, both before and after optimiations. + */ + +#include <string> +#include <iostream> +#include <sstream> +#include <getopt.h> + +#include "ast.h" +#include "ir_optimization.h" +#include "ir_print_visitor.h" +#include "program.h" +#include "ir_reader.h" +#include "standalone_scaffolding.h" + +using namespace std; + +static string read_stdin_to_eof() +{ + stringbuf sb; + cin.get(sb, '\0'); + return sb.str(); +} + +static GLboolean +do_optimization(struct exec_list *ir, const char *optimization) +{ + int int_0; + int int_1; + int int_2; + int int_3; + int int_4; + + if (sscanf(optimization, "do_common_optimization ( %d , %d ) ", + &int_0, &int_1) == 2) { + return do_common_optimization(ir, int_0 != 0, int_1); + } else if (strcmp(optimization, "do_algebraic") == 0) { + return do_algebraic(ir); + } else if (strcmp(optimization, "do_constant_folding") == 0) { + return do_constant_folding(ir); + } else if (strcmp(optimization, "do_constant_variable") == 0) { + return do_constant_variable(ir); + } else if (strcmp(optimization, "do_constant_variable_unlinked") == 0) { + return do_constant_variable_unlinked(ir); + } else if (strcmp(optimization, "do_copy_propagation") == 0) { + return do_copy_propagation(ir); + } else if (strcmp(optimization, "do_copy_propagation_elements") == 0) { + return do_copy_propagation_elements(ir); + } else if (strcmp(optimization, "do_constant_propagation") == 0) { + return do_constant_propagation(ir); + } else if (strcmp(optimization, "do_dead_code") == 0) { + return do_dead_code(ir); + } else if (strcmp(optimization, "do_dead_code_local") == 0) { + return do_dead_code_local(ir); + } else if (strcmp(optimization, "do_dead_code_unlinked") == 0) { + return do_dead_code_unlinked(ir); + } else if (strcmp(optimization, "do_dead_functions") == 0) { + return do_dead_functions(ir); + } else if (strcmp(optimization, "do_function_inlining") == 0) { + return do_function_inlining(ir); + } else if (sscanf(optimization, + "do_lower_jumps ( %d , %d , %d , %d , %d ) ", + &int_0, &int_1, &int_2, &int_3, &int_4) == 5) { + return do_lower_jumps(ir, int_0 != 0, int_1 != 0, int_2 != 0, + int_3 != 0, int_4 != 0); + } else if (strcmp(optimization, "do_lower_texture_projection") == 0) { + return do_lower_texture_projection(ir); + } else if (strcmp(optimization, "do_if_simplification") == 0) { + return do_if_simplification(ir); + } else if (strcmp(optimization, "do_discard_simplification") == 0) { + return do_discard_simplification(ir); + } else if (sscanf(optimization, "lower_if_to_cond_assign ( %d ) ", + &int_0) == 1) { + return lower_if_to_cond_assign(ir, int_0); + } else if (strcmp(optimization, "do_mat_op_to_vec") == 0) { + return do_mat_op_to_vec(ir); + } else if (strcmp(optimization, "do_noop_swizzle") == 0) { + return do_noop_swizzle(ir); + } else if (strcmp(optimization, "do_structure_splitting") == 0) { + return do_structure_splitting(ir); + } else if (strcmp(optimization, "do_swizzle_swizzle") == 0) { + return do_swizzle_swizzle(ir); + } else if (strcmp(optimization, "do_tree_grafting") == 0) { + return do_tree_grafting(ir); + } else if (strcmp(optimization, "do_vec_index_to_cond_assign") == 0) { + return do_vec_index_to_cond_assign(ir); + } else if (strcmp(optimization, "do_vec_index_to_swizzle") == 0) { + return do_vec_index_to_swizzle(ir); + } else if (strcmp(optimization, "lower_discard") == 0) { + return lower_discard(ir); + } else if (sscanf(optimization, "lower_instructions ( %d ) ", + &int_0) == 1) { + return lower_instructions(ir, int_0); + } else if (strcmp(optimization, "lower_noise") == 0) { + return lower_noise(ir); + } else if (sscanf(optimization, "lower_variable_index_to_cond_assign " + "( %d , %d , %d , %d ) ", &int_0, &int_1, &int_2, + &int_3) == 4) { + return lower_variable_index_to_cond_assign(ir, int_0 != 0, int_1 != 0, + int_2 != 0, int_3 != 0); + } else if (sscanf(optimization, "lower_quadop_vector ( %d ) ", + &int_0) == 1) { + return lower_quadop_vector(ir, int_0 != 0); + } else if (strcmp(optimization, "optimize_redundant_jumps") == 0) { + return optimize_redundant_jumps(ir); + } else { + printf("Unrecognized optimization %s\n", optimization); + exit(EXIT_FAILURE); + return false; + } +} + +static GLboolean +do_optimization_passes(struct exec_list *ir, char **optimizations, + int num_optimizations, bool quiet) +{ + GLboolean overall_progress = false; + + for (int i = 0; i < num_optimizations; ++i) { + const char *optimization = optimizations[i]; + if (!quiet) { + printf("*** Running optimization %s...", optimization); + } + GLboolean progress = do_optimization(ir, optimization); + if (!quiet) { + printf("%s\n", progress ? "progress" : "no progress"); + } + validate_ir_tree(ir); + + overall_progress = overall_progress || progress; + } + + return overall_progress; +} + +int test_optpass(int argc, char **argv) +{ + int input_format_ir = 0; /* 0=glsl, 1=ir */ + int loop = 0; + int shader_type = GL_VERTEX_SHADER; + int quiet = 0; + + const struct option optpass_opts[] = { + { "input-ir", no_argument, &input_format_ir, 1 }, + { "input-glsl", no_argument, &input_format_ir, 0 }, + { "loop", no_argument, &loop, 1 }, + { "vertex-shader", no_argument, &shader_type, GL_VERTEX_SHADER }, + { "fragment-shader", no_argument, &shader_type, GL_FRAGMENT_SHADER }, + { "quiet", no_argument, &quiet, 1 }, + { NULL, 0, NULL, 0 } + }; + + int idx = 0; + int c; + while ((c = getopt_long(argc, argv, "", optpass_opts, &idx)) != -1) { + if (c != 0) { + printf("*** usage: %s optpass <optimizations> <options>\n", argv[0]); + printf("\n"); + printf("Possible options are:\n"); + printf(" --input-ir: input format is IR\n"); + printf(" --input-glsl: input format is GLSL (the default)\n"); + printf(" --loop: run optimizations repeatedly until no progress\n"); + printf(" --vertex-shader: test with a vertex shader (the default)\n"); + printf(" --fragment-shader: test with a fragment shader\n"); + exit(EXIT_FAILURE); + } + } + + struct gl_context local_ctx; + struct gl_context *ctx = &local_ctx; + initialize_context_to_defaults(ctx, API_OPENGL); + + ctx->Driver.NewShader = _mesa_new_shader; + + struct gl_shader *shader = rzalloc(NULL, struct gl_shader); + shader->Type = shader_type; + + string input = read_stdin_to_eof(); + + struct _mesa_glsl_parse_state *state + = new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader); + + if (input_format_ir) { + shader->ir = new(shader) exec_list; + _mesa_glsl_initialize_types(state); + _mesa_glsl_read_ir(state, shader->ir, input.c_str(), true); + } else { + shader->Source = input.c_str(); + 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); + } + + shader->ir = new(shader) exec_list; + if (!state->error && !state->translation_unit.is_empty()) + _mesa_ast_to_hir(shader->ir, state); + } + + /* Print out the initial IR */ + if (!state->error && !quiet) { + printf("*** pre-optimization IR:\n"); + _mesa_print_ir(shader->ir, state); + printf("\n--\n"); + } + + /* Optimization passes */ + if (!state->error) { + GLboolean progress; + do { + progress = do_optimization_passes(shader->ir, &argv[optind], + argc - optind, quiet != 0); + } while (loop && progress); + } + + /* Print out the resulting IR */ + if (!state->error) { + if (!quiet) { + printf("*** resulting IR:\n"); + } + _mesa_print_ir(shader->ir, state); + if (!quiet) { + printf("\n--\n"); + } + } + + if (state->error) { + printf("*** error(s) occurred:\n"); + printf("%s\n", state->info_log); + printf("--\n"); + } + + ralloc_free(state); + ralloc_free(shader); + + return state->error; +} + diff --git a/src/glsl/test_optpass.h b/src/glsl/test_optpass.h new file mode 100644 index 00000000000..923ccf3dece --- /dev/null +++ b/src/glsl/test_optpass.h @@ -0,0 +1,30 @@ +/* + * Copyright © 2011 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 TEST_OPTPASS_H +#define TEST_OPTPASS_H + +int test_optpass(int argc, char **argv); + +#endif /* TEST_OPTPASS_H */ diff --git a/src/glsl/tests/compare_ir b/src/glsl/tests/compare_ir new file mode 100755 index 00000000000..a40fc810cf3 --- /dev/null +++ b/src/glsl/tests/compare_ir @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# coding=utf-8 +# +# Copyright © 2011 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. + +# Compare two files containing IR code. Ignore formatting differences +# and declaration order. + +import os +import os.path +import subprocess +import sys +import tempfile + +from sexps import * + +if len(sys.argv) != 3: + print 'Usage: compare_ir <file1> <file2>' + exit(1) + +with open(sys.argv[1]) as f: + ir1 = sort_decls(parse_sexp(f.read())) +with open(sys.argv[2]) as f: + ir2 = sort_decls(parse_sexp(f.read())) + +if ir1 == ir2: + exit(0) +else: + file1, path1 = tempfile.mkstemp(os.path.basename(sys.argv[1])) + file2, path2 = tempfile.mkstemp(os.path.basename(sys.argv[2])) + try: + os.write(file1, '{0}\n'.format(sexp_to_string(ir1))) + os.close(file1) + os.write(file2, '{0}\n'.format(sexp_to_string(ir2))) + os.close(file2) + subprocess.call(['diff', '-u', path1, path2]) + finally: + os.remove(path1) + os.remove(path2) + exit(1) diff --git a/src/glsl/tests/lower_jumps/.gitignore b/src/glsl/tests/lower_jumps/.gitignore new file mode 100644 index 00000000000..f47cb2045f1 --- /dev/null +++ b/src/glsl/tests/lower_jumps/.gitignore @@ -0,0 +1 @@ +*.out diff --git a/src/glsl/tests/lower_jumps/create_test_cases.py b/src/glsl/tests/lower_jumps/create_test_cases.py new file mode 100644 index 00000000000..fbc6f0a84ea --- /dev/null +++ b/src/glsl/tests/lower_jumps/create_test_cases.py @@ -0,0 +1,643 @@ +# coding=utf-8 +# +# Copyright © 2011 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. + +import os +import os.path +import re +import subprocess +import sys + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # For access to sexps.py, which is in parent dir +from sexps import * + +def make_test_case(f_name, ret_type, body): + """Create a simple optimization test case consisting of a single + function with the given name, return type, and body. + + Global declarations are automatically created for any undeclared + variables that are referenced by the function. All undeclared + variables are assumed to be floats. + """ + check_sexp(body) + declarations = {} + def make_declarations(sexp, already_declared = ()): + if isinstance(sexp, list): + if len(sexp) == 2 and sexp[0] == 'var_ref': + if sexp[1] not in already_declared: + declarations[sexp[1]] = [ + 'declare', ['in'], 'float', sexp[1]] + elif len(sexp) == 4 and sexp[0] == 'assign': + assert sexp[2][0] == 'var_ref' + if sexp[2][1] not in already_declared: + declarations[sexp[2][1]] = [ + 'declare', ['out'], 'float', sexp[2][1]] + make_declarations(sexp[3], already_declared) + else: + already_declared = set(already_declared) + for s in sexp: + if isinstance(s, list) and len(s) >= 4 and \ + s[0] == 'declare': + already_declared.add(s[3]) + else: + make_declarations(s, already_declared) + make_declarations(body) + return declarations.values() + \ + [['function', f_name, ['signature', ret_type, ['parameters'], body]]] + + +# The following functions can be used to build expressions. + +def const_float(value): + """Create an expression representing the given floating point value.""" + return ['constant', 'float', ['{0:.6f}'.format(value)]] + +def const_bool(value): + """Create an expression representing the given boolean value. + + If value is not a boolean, it is converted to a boolean. So, for + instance, const_bool(1) is equivalent to const_bool(True). + """ + return ['constant', 'bool', ['{0}'.format(1 if value else 0)]] + +def gt_zero(var_name): + """Create Construct the expression var_name > 0""" + return ['expression', 'bool', '>', ['var_ref', var_name], const_float(0)] + + +# The following functions can be used to build complex control flow +# statements. All of these functions return statement lists (even +# those which only create a single statement), so that statements can +# be sequenced together using the '+' operator. + +def return_(value = None): + """Create a return statement.""" + if value is not None: + return [['return', value]] + else: + return [['return']] + +def break_(): + """Create a break statement.""" + return ['break'] + +def continue_(): + """Create a continue statement.""" + return ['continue'] + +def simple_if(var_name, then_statements, else_statements = None): + """Create a statement of the form + + if (var_name > 0.0) { + <then_statements> + } else { + <else_statements> + } + + else_statements may be omitted. + """ + if else_statements is None: + else_statements = [] + check_sexp(then_statements) + check_sexp(else_statements) + return [['if', gt_zero(var_name), then_statements, else_statements]] + +def loop(statements): + """Create a loop containing the given statements as its loop + body. + """ + check_sexp(statements) + return [['loop', [], [], [], [], statements]] + +def declare_temp(var_type, var_name): + """Create a declaration of the form + + (declare (temporary) <var_type> <var_name) + """ + return [['declare', ['temporary'], var_type, var_name]] + +def assign_x(var_name, value): + """Create a statement that assigns <value> to the variable + <var_name>. The assignment uses the mask (x). + """ + check_sexp(value) + return [['assign', ['x'], ['var_ref', var_name], value]] + +def complex_if(var_prefix, statements): + """Create a statement of the form + + if (<var_prefix>a > 0.0) { + if (<var_prefix>b > 0.0) { + <statements> + } + } + + This is useful in testing jump lowering, because if <statements> + ends in a jump, lower_jumps.cpp won't try to combine this + construct with the code that follows it, as it might do for a + simple if. + + All variables used in the if statement are prefixed with + var_prefix. This can be used to ensure uniqueness. + """ + check_sexp(statements) + return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements)) + +def declare_execute_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean execute_flag. + """ + return declare_temp('bool', 'execute_flag') + \ + assign_x('execute_flag', const_bool(True)) + +def declare_return_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean return_flag. + """ + return declare_temp('bool', 'return_flag') + \ + assign_x('return_flag', const_bool(False)) + +def declare_return_value(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary variable return_value. Assume that + return_value is a float. + """ + return declare_temp('float', 'return_value') + +def declare_break_flag(): + """Create the statements that lower_jumps.cpp uses to declare and + initialize the temporary boolean break_flag. + """ + return declare_temp('bool', 'break_flag') + \ + assign_x('break_flag', const_bool(False)) + +def lowered_return_simple(value = None): + """Create the statements that lower_jumps.cpp lowers a return + statement to, in situations where it does not need to clear the + execute flag. + """ + if value: + result = assign_x('return_value', value) + else: + result = [] + return result + assign_x('return_flag', const_bool(True)) + +def lowered_return(value = None): + """Create the statements that lower_jumps.cpp lowers a return + statement to, in situations where it needs to clear the execute + flag. + """ + return lowered_return_simple(value) + \ + assign_x('execute_flag', const_bool(False)) + +def lowered_continue(): + """Create the statement that lower_jumps.cpp lowers a continue + statement to. + """ + return assign_x('execute_flag', const_bool(False)) + +def lowered_break_simple(): + """Create the statement that lower_jumps.cpp lowers a break + statement to, in situations where it does not need to clear the + execute flag. + """ + return assign_x('break_flag', const_bool(True)) + +def lowered_break(): + """Create the statement that lower_jumps.cpp lowers a break + statement to, in situations where it needs to clear the execute + flag. + """ + return lowered_break_simple() + assign_x('execute_flag', const_bool(False)) + +def if_execute_flag(statements): + """Wrap statements in an if test so that they will only execute if + execute_flag is True. + """ + check_sexp(statements) + return [['if', ['var_ref', 'execute_flag'], statements, []]] + +def if_not_return_flag(statements): + """Wrap statements in an if test so that they will only execute if + return_flag is False. + """ + check_sexp(statements) + return [['if', ['var_ref', 'return_flag'], [], statements]] + +def final_return(): + """Create the return statement that lower_jumps.cpp places at the + end of a function when lowering returns. + """ + return [['return', ['var_ref', 'return_value']]] + +def final_break(): + """Create the conditional break statement that lower_jumps.cpp + places at the end of a function when lowering breaks. + """ + return [['if', ['var_ref', 'break_flag'], break_(), []]] + +def bash_quote(*args): + """Quote the arguments appropriately so that bash will understand + each argument as a single word. + """ + def quote_word(word): + for c in word: + if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'): + break + else: + if not word: + return "''" + return word + return "'{0}'".format(word.replace("'", "'\"'\"'")) + return ' '.join(quote_word(word) for word in args) + +def create_test_case(doc_string, input_sexp, expected_sexp, test_name, + pull_out_jumps=False, lower_sub_return=False, + lower_main_return=False, lower_continue=False, + lower_break=False): + """Create a test case that verifies that do_lower_jumps transforms + the given code in the expected way. + """ + doc_lines = [line.strip() for line in doc_string.splitlines()] + doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '') + check_sexp(input_sexp) + check_sexp(expected_sexp) + input_str = sexp_to_string(sort_decls(input_sexp)) + expected_output = sexp_to_string(sort_decls(expected_sexp)) + + optimization = ( + 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format( + pull_out_jumps, lower_sub_return, lower_main_return, + lower_continue, lower_break)) + args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization] + test_file = '{0}.opt_test'.format(test_name) + with open(test_file, 'w') as f: + f.write('#!/bin/bash\n#\n# This file was generated by create_test_cases.py.\n#\n') + f.write(doc_string) + f.write('{0} <<EOF\n'.format(bash_quote(*args))) + f.write('{0}\nEOF\n'.format(input_str)) + os.chmod(test_file, 0774) + expected_file = '{0}.opt_test.expected'.format(test_name) + with open(expected_file, 'w') as f: + f.write('{0}\n'.format(expected_output)) + +def test_lower_returns_main(): + doc_string = """Test that do_lower_jumps respects the lower_main_return + flag in deciding whether to lower returns in the main + function. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('', return_()) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('', lowered_return()) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_main_true', + lower_main_return=True) + create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_main_false', + lower_main_return=False) + +def test_lower_returns_sub(): + doc_string = """Test that do_lower_jumps respects the lower_sub_return flag + in deciding whether to lower returns in subroutines. + """ + input_sexp = make_test_case('sub', 'void', ( + complex_if('', return_()) + )) + expected_sexp = make_test_case('sub', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('', lowered_return()) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_sub_true', + lower_sub_return=True) + create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_sub_false', + lower_sub_return=False) + +def test_lower_returns_1(): + doc_string = """Test that a void return at the end of a function is + eliminated. + """ + input_sexp = make_test_case('main', 'void', ( + assign_x('a', const_float(1)) + + return_() + )) + expected_sexp = make_test_case('main', 'void', ( + assign_x('a', const_float(1)) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_1', + lower_main_return=True) + +def test_lower_returns_2(): + doc_string = """Test that lowering is not performed on a non-void return at + the end of subroutine. + """ + input_sexp = make_test_case('sub', 'float', ( + assign_x('a', const_float(1)) + + return_(const_float(1)) + )) + create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_2', + lower_sub_return=True) + +def test_lower_returns_3(): + doc_string = """Test lowering of returns when there is one nested inside a + complex structure of ifs, and one at the end of a function. + + In this case, the latter return needs to be lowered because it + will not be at the end of the function once the final return + is inserted. + """ + input_sexp = make_test_case('sub', 'float', ( + complex_if('', return_(const_float(1))) + + return_(const_float(2)) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + complex_if('', lowered_return(const_float(1))) + + if_execute_flag(lowered_return(const_float(2))) + + final_return() + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_3', + lower_sub_return=True) + +def test_lower_returns_4(): + doc_string = """Test that returns are properly lowered when they occur in + both branches of an if-statement. + """ + input_sexp = make_test_case('sub', 'float', ( + simple_if('a', return_(const_float(1)), + return_(const_float(2))) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + simple_if('a', lowered_return(const_float(1)), + lowered_return(const_float(2))) + + final_return() + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_4', + lower_sub_return=True) + +def test_lower_unified_returns(): + doc_string = """If both branches of an if statement end in a return, and + pull_out_jumps is True, then those returns should be lifted + outside the if and then properly lowered. + + Verify that this lowering occurs during the same pass as the + lowering of other returns by checking that extra temporary + variables aren't generated. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('a', return_()) + + simple_if('b', simple_if('c', return_(), return_())) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('a', lowered_return()) + + if_execute_flag(simple_if('b', (simple_if('c', [], []) + + lowered_return()))) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_unified_returns', + lower_main_return=True, pull_out_jumps=True) + +def test_lower_pulled_out_jump(): + doc_string = """If one branch of an if ends in a jump, and control cannot + fall out the bottom of the other branch, and pull_out_jumps is + True, then the jump is lifted outside the if. + + Verify that this lowering occurs during the same pass as the + lowering of other jumps by checking that extra temporary + variables aren't generated. + """ + input_sexp = make_test_case('main', 'void', ( + complex_if('a', return_()) + + loop(simple_if('b', simple_if('c', break_(), continue_()), + return_())) + + assign_x('d', const_float(1)) + )) + # Note: optimization produces two other effects: the break + # gets lifted out of the if statements, and the code after the + # loop gets guarded so that it only executes if the return + # flag is clear. + expected_sexp = make_test_case('main', 'void', ( + declare_execute_flag() + + declare_return_flag() + + complex_if('a', lowered_return()) + + if_execute_flag( + loop(simple_if('b', simple_if('c', [], continue_()), + lowered_return_simple()) + + break_()) + + if_not_return_flag(assign_x('d', const_float(1)))) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_pulled_out_jump', + lower_main_return=True, pull_out_jumps=True) + +def test_lower_breaks_1(): + doc_string = """If a loop contains an unconditional break at the bottom of + it, it should not be lowered.""" + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + break_()) + )) + expected_sexp = input_sexp + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_1', lower_break=True) + +def test_lower_breaks_2(): + doc_string = """If a loop contains a conditional break at the bottom of it, + it should not be lowered if it is in the then-clause. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', break_())) + )) + expected_sexp = input_sexp + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_2', lower_break=True) + +def test_lower_breaks_3(): + doc_string = """If a loop contains a conditional break at the bottom of it, + it should not be lowered if it is in the then-clause, even if + there are statements preceding the break. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', (assign_x('c', const_float(1)) + + break_()))) + )) + expected_sexp = input_sexp + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_3', lower_break=True) + +def test_lower_breaks_4(): + doc_string = """If a loop contains a conditional break at the bottom of it, + it should not be lowered if it is in the else-clause. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', [], break_())) + )) + expected_sexp = input_sexp + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_4', lower_break=True) + +def test_lower_breaks_5(): + doc_string = """If a loop contains a conditional break at the bottom of it, + it should not be lowered if it is in the else-clause, even if + there are statements preceding the break. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + simple_if('b', [], (assign_x('c', const_float(1)) + + break_()))) + )) + expected_sexp = input_sexp + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_5', lower_break=True) + +def test_lower_breaks_6(): + doc_string = """If a loop contains conditional breaks and continues, and + ends in an unconditional break, then the unconditional break + needs to be lowered, because it will no longer be at the end + of the loop after the final break is added. + """ + input_sexp = make_test_case('main', 'void', ( + loop(simple_if('a', (complex_if('b', continue_()) + + complex_if('c', break_()))) + + break_()) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_break_flag() + + loop(declare_execute_flag() + + simple_if( + 'a', + (complex_if('b', lowered_continue()) + + if_execute_flag( + complex_if('c', lowered_break())))) + + if_execute_flag(lowered_break_simple()) + + final_break()) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_6', + lower_break=True, lower_continue=True) + +def test_lower_guarded_conditional_break(): + doc_string = """Normally a conditional break at the end of a loop isn't + lowered, however if the conditional break gets placed inside + an if(execute_flag) because of earlier lowering of continues, + then the break needs to be lowered. + """ + input_sexp = make_test_case('main', 'void', ( + loop(complex_if('a', continue_()) + + simple_if('b', break_())) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_break_flag() + + loop(declare_execute_flag() + + complex_if('a', lowered_continue()) + + if_execute_flag(simple_if('b', lowered_break())) + + final_break()) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'lower_guarded_conditional_break', + lower_break=True, lower_continue=True) + +def test_remove_continue_at_end_of_loop(): + doc_string = """Test that a redundant continue-statement at the end of a + loop is removed. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + continue_()) + )) + expected_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1))) + )) + create_test_case(doc_string, input_sexp, expected_sexp, 'remove_continue_at_end_of_loop') + +def test_lower_return_void_at_end_of_loop(): + doc_string = """Test that a return of void at the end of a loop is properly + lowered. + """ + input_sexp = make_test_case('main', 'void', ( + loop(assign_x('a', const_float(1)) + + return_()) + + assign_x('b', const_float(2)) + )) + expected_sexp = make_test_case('main', 'void', ( + declare_return_flag() + + loop(assign_x('a', const_float(1)) + + lowered_return_simple() + + break_()) + + if_not_return_flag(assign_x('b', const_float(2))) + )) + create_test_case(doc_string, input_sexp, input_sexp, 'return_void_at_end_of_loop_lower_nothing') + create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return', + lower_main_return=True) + create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return_and_break', + lower_main_return=True, lower_break=True) + +def test_lower_return_non_void_at_end_of_loop(): + doc_string = """Test that a non-void return at the end of a loop is + properly lowered. + """ + input_sexp = make_test_case('sub', 'float', ( + loop(assign_x('a', const_float(1)) + + return_(const_float(2))) + + assign_x('b', const_float(3)) + + return_(const_float(4)) + )) + expected_sexp = make_test_case('sub', 'float', ( + declare_execute_flag() + + declare_return_value() + + declare_return_flag() + + loop(assign_x('a', const_float(1)) + + lowered_return_simple(const_float(2)) + + break_()) + + if_not_return_flag(assign_x('b', const_float(3)) + + lowered_return(const_float(4))) + + final_return() + )) + create_test_case(doc_string, input_sexp, input_sexp, 'return_non_void_at_end_of_loop_lower_nothing') + create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return', + lower_sub_return=True) + create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return_and_break', + lower_sub_return=True, lower_break=True) + +if __name__ == '__main__': + test_lower_returns_main() + test_lower_returns_sub() + test_lower_returns_1() + test_lower_returns_2() + test_lower_returns_3() + test_lower_returns_4() + test_lower_unified_returns() + test_lower_pulled_out_jump() + test_lower_breaks_1() + test_lower_breaks_2() + test_lower_breaks_3() + test_lower_breaks_4() + test_lower_breaks_5() + test_lower_breaks_6() + test_lower_guarded_conditional_break() + test_remove_continue_at_end_of_loop() + test_lower_return_void_at_end_of_loop() + test_lower_return_non_void_at_end_of_loop() diff --git a/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test new file mode 100755 index 00000000000..01ad7087a28 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test @@ -0,0 +1,13 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains an unconditional break at the bottom of +# it, it should not be lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 1)' <<EOF +((declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) break)))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test.expected new file mode 100644 index 00000000000..d4bb6fc0274 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_1.opt_test.expected @@ -0,0 +1,5 @@ +((declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) break)))))) diff --git a/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test new file mode 100755 index 00000000000..0be22f953e1 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test @@ -0,0 +1,15 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains a conditional break at the bottom of it, +# it should not be lowered if it is in the then-clause. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 1)' <<EOF +((declare (in) float b) (declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) (break) + ()))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test.expected new file mode 100644 index 00000000000..a4cb2d6a125 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_2.opt_test.expected @@ -0,0 +1,7 @@ +((declare (in) float b) (declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) (break) + ()))))))) diff --git a/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test new file mode 100755 index 00000000000..4149360b5d0 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test @@ -0,0 +1,17 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains a conditional break at the bottom of it, +# it should not be lowered if it is in the then-clause, even if +# there are statements preceding the break. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 1)' <<EOF +((declare (in) float b) (declare (out) float a) (declare (out) float c) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref c) (constant float (1.000000))) break) + ()))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test.expected new file mode 100644 index 00000000000..325f7b49a5d --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_3.opt_test.expected @@ -0,0 +1,8 @@ +((declare (in) float b) (declare (out) float a) (declare (out) float c) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref c) (constant float (1.000000))) break) + ()))))))) diff --git a/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test new file mode 100755 index 00000000000..70458bb4f8e --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test @@ -0,0 +1,15 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains a conditional break at the bottom of it, +# it should not be lowered if it is in the else-clause. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 1)' <<EOF +((declare (in) float b) (declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) () + (break)))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test.expected new file mode 100644 index 00000000000..a7735457cb8 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_4.opt_test.expected @@ -0,0 +1,7 @@ +((declare (in) float b) (declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) () + (break)))))))) diff --git a/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test new file mode 100755 index 00000000000..da9eef1105e --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains a conditional break at the bottom of it, +# it should not be lowered if it is in the else-clause, even if +# there are statements preceding the break. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 1)' <<EOF +((declare (in) float b) (declare (out) float a) (declare (out) float c) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) () + ((assign (x) (var_ref c) (constant float (1.000000))) break)))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test.expected new file mode 100644 index 00000000000..0dd4a529383 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_5.opt_test.expected @@ -0,0 +1,7 @@ +((declare (in) float b) (declare (out) float a) (declare (out) float c) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (if (expression bool > (var_ref b) (constant float (0.000000))) () + ((assign (x) (var_ref c) (constant float (1.000000))) break)))))))) diff --git a/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test b/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test new file mode 100755 index 00000000000..9440dfec897 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test @@ -0,0 +1,29 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If a loop contains conditional breaks and continues, and +# ends in an unconditional break, then the unconditional break +# needs to be lowered, because it will no longer be at the end +# of the loop after the final break is added. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 1, 1)' <<EOF +((declare (in) float a) (declare (in) float ba) (declare (in) float bb) + (declare (in) float ca) + (declare (in) float cb) + (function main + (signature void (parameters) + ((loop () () () () + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref ba) (constant float (0.000000))) + ((if (expression bool > (var_ref bb) (constant float (0.000000))) + (continue) + ())) + ()) + (if (expression bool > (var_ref ca) (constant float (0.000000))) + ((if (expression bool > (var_ref cb) (constant float (0.000000))) + (break) + ())) + ())) + ()) + break)))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test.expected b/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test.expected new file mode 100644 index 00000000000..8222328e00c --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_breaks_6.opt_test.expected @@ -0,0 +1,29 @@ +((declare (in) float a) (declare (in) float ba) (declare (in) float bb) + (declare (in) float ca) + (declare (in) float cb) + (function main + (signature void (parameters) + ((declare (temporary) bool break_flag) + (assign (x) (var_ref break_flag) (constant bool (0))) + (loop () () () () + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref ba) (constant float (0.000000))) + ((if (expression bool > (var_ref bb) (constant float (0.000000))) + ((assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref execute_flag) + ((if (expression bool > (var_ref ca) (constant float (0.000000))) + ((if (expression bool > (var_ref cb) (constant float (0.000000))) + ((assign (x) (var_ref break_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ())) + ())) + ()) + (if (var_ref execute_flag) + ((assign (x) (var_ref break_flag) (constant bool (1)))) + ()) + (if (var_ref break_flag) (break) ()))))))) diff --git a/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test b/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test new file mode 100755 index 00000000000..379aa59b5a2 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Normally a conditional break at the end of a loop isn't +# lowered, however if the conditional break gets placed inside +# an if(execute_flag) because of earlier lowering of continues, +# then the break needs to be lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 1, 1)' <<EOF +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (function main + (signature void (parameters) + ((loop () () () () + ((if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + (continue) + ())) + ()) + (if (expression bool > (var_ref b) (constant float (0.000000))) (break) + ()))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test.expected b/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test.expected new file mode 100644 index 00000000000..7c6e73f77f8 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test.expected @@ -0,0 +1,20 @@ +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (function main + (signature void (parameters) + ((declare (temporary) bool break_flag) + (assign (x) (var_ref break_flag) (constant bool (0))) + (loop () () () () + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + ((assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref execute_flag) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref break_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref break_flag) (break) ()))))))) diff --git a/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test b/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test new file mode 100755 index 00000000000..15f3c41d5a2 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test @@ -0,0 +1,28 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If one branch of an if ends in a jump, and control cannot +# fall out the bottom of the other branch, and pull_out_jumps is +# True, then the jump is lifted outside the if. +# Verify that this lowering occurs during the same pass as the +# lowering of other jumps by checking that extra temporary +# variables aren't generated. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(1, 0, 1, 0, 0)' <<EOF +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (declare (in) float c) + (declare (out) float d) + (function main + (signature void (parameters) + ((if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + ((return)) + ())) + ()) + (loop () () () () + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((if (expression bool > (var_ref c) (constant float (0.000000))) (break) + (continue))) + ((return))))) + (assign (x) (var_ref d) (constant float (1.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test.expected b/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test.expected new file mode 100644 index 00000000000..bf45c2c93b6 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test.expected @@ -0,0 +1,25 @@ +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (declare (in) float c) + (declare (out) float d) + (function main + (signature void (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + ((assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref execute_flag) + ((loop () () () () + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((if (expression bool > (var_ref c) (constant float (0.000000))) () + (continue))) + ((assign (x) (var_ref return_flag) (constant bool (1))))) + break)) + (if (var_ref return_flag) () + ((assign (x) (var_ref d) (constant float (1.000000)))))) + ()))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_1.opt_test b/src/glsl/tests/lower_jumps/lower_returns_1.opt_test new file mode 100755 index 00000000000..a1f895bbf78 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_1.opt_test @@ -0,0 +1,12 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a void return at the end of a function is +# eliminated. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 1, 0, 0)' <<EOF +((declare (out) float a) + (function main + (signature void (parameters) + ((assign (x) (var_ref a) (constant float (1.000000))) (return))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_1.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_1.opt_test.expected new file mode 100644 index 00000000000..7c3919c016e --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_1.opt_test.expected @@ -0,0 +1,4 @@ +((declare (out) float a) + (function main + (signature void (parameters) + ((assign (x) (var_ref a) (constant float (1.000000))))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_2.opt_test b/src/glsl/tests/lower_jumps/lower_returns_2.opt_test new file mode 100755 index 00000000000..61673d4ef66 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_2.opt_test @@ -0,0 +1,13 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that lowering is not performed on a non-void return at +# the end of subroutine. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 0)' <<EOF +((declare (out) float a) + (function sub + (signature float (parameters) + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (1.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_2.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_2.opt_test.expected new file mode 100644 index 00000000000..7777927f5a3 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_2.opt_test.expected @@ -0,0 +1,5 @@ +((declare (out) float a) + (function sub + (signature float (parameters) + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (1.000000))))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_3.opt_test b/src/glsl/tests/lower_jumps/lower_returns_3.opt_test new file mode 100755 index 00000000000..9881e249270 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_3.opt_test @@ -0,0 +1,20 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test lowering of returns when there is one nested inside a +# complex structure of ifs, and one at the end of a function. +# In this case, the latter return needs to be lowered because it +# will not be at the end of the function once the final return +# is inserted. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 0)' <<EOF +((declare (in) float a) (declare (in) float b) + (function sub + (signature float (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return (constant float (1.000000)))) + ())) + ()) + (return (constant float (2.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_3.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_3.opt_test.expected new file mode 100644 index 00000000000..d4835e96b7c --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_3.opt_test.expected @@ -0,0 +1,21 @@ +((declare (in) float a) (declare (in) float b) + (function sub + (signature float (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) float return_value) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref return_value) (constant float (1.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref execute_flag) + ((assign (x) (var_ref return_value) (constant float (2.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ()) + (return (var_ref return_value)))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_4.opt_test b/src/glsl/tests/lower_jumps/lower_returns_4.opt_test new file mode 100755 index 00000000000..9f54c67a180 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_4.opt_test @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that returns are properly lowered when they occur in +# both branches of an if-statement. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 0)' <<EOF +((declare (in) float a) + (function sub + (signature float (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((return (constant float (1.000000)))) + ((return (constant float (2.000000))))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_4.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_4.opt_test.expected new file mode 100644 index 00000000000..b551a066f43 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_4.opt_test.expected @@ -0,0 +1,16 @@ +((declare (in) float a) + (function sub + (signature float (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) float return_value) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref a) (constant float (0.000000))) + ((assign (x) (var_ref return_value) (constant float (1.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ((assign (x) (var_ref return_value) (constant float (2.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0))))) + (return (var_ref return_value)))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test b/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test new file mode 100755 index 00000000000..5f97bfd3f5a --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test @@ -0,0 +1,17 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that do_lower_jumps respects the lower_main_return +# flag in deciding whether to lower returns in the main +# function. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 0)' <<EOF +((declare (in) float a) (declare (in) float b) + (function main + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test.expected new file mode 100644 index 00000000000..e8b36f14478 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test.expected @@ -0,0 +1,8 @@ +((declare (in) float a) (declare (in) float b) + (function main + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test b/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test new file mode 100755 index 00000000000..59c7ba1dd52 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test @@ -0,0 +1,17 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that do_lower_jumps respects the lower_main_return +# flag in deciding whether to lower returns in the main +# function. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 1, 0, 0)' <<EOF +((declare (in) float a) (declare (in) float b) + (function main + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test.expected new file mode 100644 index 00000000000..e15a97d1db2 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test.expected @@ -0,0 +1,13 @@ +((declare (in) float a) (declare (in) float b) + (function main + (signature void (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test b/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test new file mode 100755 index 00000000000..40e784e3318 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that do_lower_jumps respects the lower_sub_return flag +# in deciding whether to lower returns in subroutines. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 0)' <<EOF +((declare (in) float a) (declare (in) float b) + (function sub + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test.expected new file mode 100644 index 00000000000..07db6e708f4 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test.expected @@ -0,0 +1,8 @@ +((declare (in) float a) (declare (in) float b) + (function sub + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) diff --git a/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test b/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test new file mode 100755 index 00000000000..9fe6b90f085 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that do_lower_jumps respects the lower_sub_return flag +# in deciding whether to lower returns in subroutines. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 0)' <<EOF +((declare (in) float a) (declare (in) float b) + (function sub + (signature void (parameters) + ((if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((return)) + ())) + ()))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test.expected b/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test.expected new file mode 100644 index 00000000000..31109802351 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test.expected @@ -0,0 +1,13 @@ +((declare (in) float a) (declare (in) float b) + (function sub + (signature void (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref a) (constant float (0.000000))) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()))))) diff --git a/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test b/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test new file mode 100755 index 00000000000..e7168131487 --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test @@ -0,0 +1,26 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# If both branches of an if statement end in a return, and +# pull_out_jumps is True, then those returns should be lifted +# outside the if and then properly lowered. +# Verify that this lowering occurs during the same pass as the +# lowering of other returns by checking that extra temporary +# variables aren't generated. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(1, 0, 1, 0, 0)' <<EOF +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (declare (in) float c) + (function main + (signature void (parameters) + ((if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + ((return)) + ())) + ()) + (if (expression bool > (var_ref b) (constant float (0.000000))) + ((if (expression bool > (var_ref c) (constant float (0.000000))) + ((return)) + ((return)))) + ()))))) +EOF diff --git a/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test.expected b/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test.expected new file mode 100644 index 00000000000..271cd3b462e --- /dev/null +++ b/src/glsl/tests/lower_jumps/lower_unified_returns.opt_test.expected @@ -0,0 +1,21 @@ +((declare (in) float aa) (declare (in) float ab) (declare (in) float b) + (declare (in) float c) + (function main + (signature void (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (if (expression bool > (var_ref aa) (constant float (0.000000))) + ((if (expression bool > (var_ref ab) (constant float (0.000000))) + ((assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()) + (if (var_ref execute_flag) + ((if (expression bool > (var_ref b) (constant float (0.000000))) + ((if (expression bool > (var_ref c) (constant float (0.000000))) () ()) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0)))) + ())) + ()))))) diff --git a/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test b/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test new file mode 100755 index 00000000000..18efc37f6e1 --- /dev/null +++ b/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test @@ -0,0 +1,13 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a redundant continue-statement at the end of a +# loop is removed. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 0)' <<EOF +((declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) continue)))))) +EOF diff --git a/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test.expected b/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test.expected new file mode 100644 index 00000000000..d2a02c6f380 --- /dev/null +++ b/src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test.expected @@ -0,0 +1,5 @@ +((declare (out) float a) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))))))))) diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test new file mode 100755 index 00000000000..79c0e824512 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a non-void return at the end of a loop is +# properly lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 0)' <<EOF +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (2.000000))))) + (assign (x) (var_ref b) (constant float (3.000000))) + (return (constant float (4.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test.expected b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test.expected new file mode 100644 index 00000000000..2cf117a5ee1 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test.expected @@ -0,0 +1,8 @@ +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (2.000000))))) + (assign (x) (var_ref b) (constant float (3.000000))) + (return (constant float (4.000000))))))) diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test new file mode 100755 index 00000000000..920d2ad9fba --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a non-void return at the end of a loop is +# properly lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 0)' <<EOF +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (2.000000))))) + (assign (x) (var_ref b) (constant float (3.000000))) + (return (constant float (4.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test.expected b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test.expected new file mode 100644 index 00000000000..0bab8f16f30 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test.expected @@ -0,0 +1,19 @@ +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) float return_value) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (assign (x) (var_ref return_value) (constant float (2.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + break)) + (if (var_ref return_flag) () + ((assign (x) (var_ref b) (constant float (3.000000))) + (assign (x) (var_ref return_value) (constant float (4.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0))))) + (return (var_ref return_value)))))) diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test new file mode 100755 index 00000000000..99f1f863506 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test @@ -0,0 +1,16 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a non-void return at the end of a loop is +# properly lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 1, 0, 0, 1)' <<EOF +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (return (constant float (2.000000))))) + (assign (x) (var_ref b) (constant float (3.000000))) + (return (constant float (4.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test.expected b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test.expected new file mode 100644 index 00000000000..0bab8f16f30 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test.expected @@ -0,0 +1,19 @@ +((declare (out) float a) (declare (out) float b) + (function sub + (signature float (parameters) + ((declare (temporary) bool execute_flag) + (assign (x) (var_ref execute_flag) (constant bool (1))) + (declare (temporary) float return_value) + (declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (assign (x) (var_ref return_value) (constant float (2.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + break)) + (if (var_ref return_flag) () + ((assign (x) (var_ref b) (constant float (3.000000))) + (assign (x) (var_ref return_value) (constant float (4.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + (assign (x) (var_ref execute_flag) (constant bool (0))))) + (return (var_ref return_value)))))) diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test new file mode 100755 index 00000000000..63487d32691 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a return of void at the end of a loop is properly +# lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 0, 0, 0)' <<EOF +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) (return))) + (assign (x) (var_ref b) (constant float (2.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test.expected b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test.expected new file mode 100644 index 00000000000..0bd8037bf00 --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test.expected @@ -0,0 +1,6 @@ +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) (return))) + (assign (x) (var_ref b) (constant float (2.000000))))))) diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test new file mode 100755 index 00000000000..523c92a686d --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a return of void at the end of a loop is properly +# lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 1, 0, 0)' <<EOF +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) (return))) + (assign (x) (var_ref b) (constant float (2.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test.expected b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test.expected new file mode 100644 index 00000000000..53814eaacad --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test.expected @@ -0,0 +1,11 @@ +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + break)) + (if (var_ref return_flag) () + ((assign (x) (var_ref b) (constant float (2.000000))))))))) diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test new file mode 100755 index 00000000000..22b5581cbda --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test @@ -0,0 +1,14 @@ +#!/bin/bash +# +# This file was generated by create_test_cases.py. +# +# Test that a return of void at the end of a loop is properly +# lowered. +../../glsl_test optpass --quiet --input-ir 'do_lower_jumps(0, 0, 1, 0, 1)' <<EOF +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) (return))) + (assign (x) (var_ref b) (constant float (2.000000))))))) +EOF diff --git a/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test.expected b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test.expected new file mode 100644 index 00000000000..53814eaacad --- /dev/null +++ b/src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test.expected @@ -0,0 +1,11 @@ +((declare (out) float a) (declare (out) float b) + (function main + (signature void (parameters) + ((declare (temporary) bool return_flag) + (assign (x) (var_ref return_flag) (constant bool (0))) + (loop () () () () + ((assign (x) (var_ref a) (constant float (1.000000))) + (assign (x) (var_ref return_flag) (constant bool (1))) + break)) + (if (var_ref return_flag) () + ((assign (x) (var_ref b) (constant float (2.000000))))))))) diff --git a/src/glsl/tests/optimization-test b/src/glsl/tests/optimization-test new file mode 100755 index 00000000000..0c130be1379 --- /dev/null +++ b/src/glsl/tests/optimization-test @@ -0,0 +1,28 @@ +#!/bin/bash + +total=0 +pass=0 + +echo "====== Testing optimization passes ======" +for test in `find . -iname '*.opt_test'`; do + echo -n "Testing $test..." + (cd `dirname "$test"`; ./`basename "$test"`) > "$test.out" 2>&1 + total=$((total+1)) + if ./compare_ir "$test.expected" "$test.out" >/dev/null 2>&1; then + echo "PASS" + pass=$((pass+1)) + else + echo "FAIL" + ./compare_ir "$test.expected" "$test.out" + fi +done + +echo "" +echo "$pass/$total tests returned correct results" +echo "" + +if [[ $pass == $total ]]; then + exit 0 +else + exit 1 +fi diff --git a/src/glsl/tests/sexps.py b/src/glsl/tests/sexps.py new file mode 100644 index 00000000000..a714af8d236 --- /dev/null +++ b/src/glsl/tests/sexps.py @@ -0,0 +1,103 @@ +# coding=utf-8 +# +# Copyright © 2011 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. + +# This file contains helper functions for manipulating sexps in Python. +# +# We represent a sexp in Python using nested lists containing strings. +# So, for example, the sexp (constant float (1.000000)) is represented +# as ['constant', 'float', ['1.000000']]. + +import re + +def check_sexp(sexp): + """Verify that the argument is a proper sexp. + + That is, raise an exception if the argument is not a string or a + list, or if it contains anything that is not a string or a list at + any nesting level. + """ + if isinstance(sexp, list): + for s in sexp: + check_sexp(s) + elif not isinstance(sexp, basestring): + raise Exception('Not a sexp: {0!r}'.format(sexp)) + +def parse_sexp(sexp): + """Convert a string, of the form that would be output by mesa, + into a sexp represented as nested lists containing strings. + """ + sexp_token_regexp = re.compile( + '[a-zA-Z_]+(@[0-9]+)?|[0-9]+(\\.[0-9]+)?|[^ \n]') + stack = [[]] + for match in sexp_token_regexp.finditer(sexp): + token = match.group(0) + if token == '(': + stack.append([]) + elif token == ')': + if len(stack) == 1: + raise Exception('Unmatched )') + sexp = stack.pop() + stack[-1].append(sexp) + else: + stack[-1].append(token) + if len(stack) != 1: + raise Exception('Unmatched (') + if len(stack[0]) != 1: + raise Exception('Multiple sexps') + return stack[0][0] + +def sexp_to_string(sexp): + """Convert a sexp, represented as nested lists containing strings, + into a single string of the form parseable by mesa. + """ + if isinstance(sexp, basestring): + return sexp + assert isinstance(sexp, list) + result = '' + for s in sexp: + sub_result = sexp_to_string(s) + if result == '': + result = sub_result + elif '\n' not in result and '\n' not in sub_result and \ + len(result) + len(sub_result) + 1 <= 70: + result += ' ' + sub_result + else: + result += '\n' + sub_result + return '({0})'.format(result.replace('\n', '\n ')) + +def sort_decls(sexp): + """Sort all toplevel variable declarations in sexp. + + This is used to work around the fact that + ir_reader::read_instructions reorders declarations. + """ + assert isinstance(sexp, list) + decls = [] + other_code = [] + for s in sexp: + if isinstance(s, list) and len(s) >= 4 and s[0] == 'declare': + decls.append(s) + else: + other_code.append(s) + return sorted(decls) + other_code + |