summaryrefslogtreecommitdiffstats
path: root/src/glsl
diff options
context:
space:
mode:
authorStéphane Marchesin <[email protected]>2011-08-26 17:37:25 -0700
committerStéphane Marchesin <[email protected]>2011-08-26 17:37:25 -0700
commitf8e6d19f3f40931be741b44d3edf210c38e13f0f (patch)
treee99e4c619901412ac6448534b0f57ce1c4295c6b /src/glsl
parent974c49ed176de55aadb335a2956ef5dfec774a23 (diff)
parente3b0e3776646d0367206e4544229622eb22fe9f8 (diff)
Merge branch 'master' of git://anongit.freedesktop.org/mesa/mesa
Diffstat (limited to 'src/glsl')
-rw-r--r--src/glsl/.gitignore1
-rw-r--r--src/glsl/Android.gen.mk98
-rw-r--r--src/glsl/Android.mk171
-rw-r--r--src/glsl/Makefile29
-rw-r--r--src/glsl/SConscript2
-rw-r--r--src/glsl/TODO21
-rw-r--r--src/glsl/ast.h3
-rw-r--r--src/glsl/ast_function.cpp114
-rw-r--r--src/glsl/ast_to_hir.cpp77
-rw-r--r--src/glsl/builtins/ir/asin68
-rw-r--r--src/glsl/builtins/ir/atan4
-rw-r--r--src/glsl/builtins/ir/radians8
-rw-r--r--src/glsl/builtins/profiles/130.frag2
-rw-r--r--src/glsl/builtins/profiles/130.vert2
-rwxr-xr-xsrc/glsl/builtins/tools/texture_builtins.py50
-rw-r--r--src/glsl/glcpp/glcpp-parse.y4
-rw-r--r--src/glsl/glsl_parser.yy14
-rw-r--r--src/glsl/glsl_parser_extras.cpp1
-rw-r--r--src/glsl/glsl_parser_extras.h8
-rw-r--r--src/glsl/glsl_types.cpp16
-rw-r--r--src/glsl/glsl_types.h35
-rw-r--r--src/glsl/ir.cpp20
-rw-r--r--src/glsl/ir.h54
-rw-r--r--src/glsl/ir_clone.cpp4
-rw-r--r--src/glsl/ir_function.cpp134
-rw-r--r--src/glsl/ir_function_detect_recursion.cpp370
-rw-r--r--src/glsl/ir_hv_accept.cpp9
-rw-r--r--src/glsl/ir_optimization.h4
-rw-r--r--src/glsl/ir_print_visitor.cpp21
-rw-r--r--src/glsl/ir_reader.cpp37
-rw-r--r--src/glsl/ir_rvalue_visitor.cpp1
-rw-r--r--src/glsl/ir_validate.cpp41
-rw-r--r--src/glsl/link_functions.cpp10
-rw-r--r--src/glsl/linker.cpp209
-rw-r--r--src/glsl/linker.h3
-rw-r--r--src/glsl/lower_if_to_cond_assign.cpp141
-rw-r--r--src/glsl/lower_instructions.cpp4
-rw-r--r--src/glsl/lower_variable_index_to_cond_assign.cpp309
-rw-r--r--src/glsl/lower_vec_index_to_cond_assign.cpp91
-rw-r--r--src/glsl/main.cpp62
-rw-r--r--src/glsl/opt_dead_functions.cpp11
-rw-r--r--src/glsl/opt_tree_grafting.cpp1
-rw-r--r--src/glsl/program.h8
-rw-r--r--src/glsl/ralloc.c5
-rw-r--r--src/glsl/s_expression.cpp64
-rw-r--r--src/glsl/s_expression.h2
-rw-r--r--src/glsl/standalone_scaffolding.cpp93
-rw-r--r--src/glsl/standalone_scaffolding.h54
-rw-r--r--src/glsl/test.cpp78
-rw-r--r--src/glsl/test_optpass.cpp273
-rw-r--r--src/glsl/test_optpass.h30
-rwxr-xr-xsrc/glsl/tests/compare_ir59
-rw-r--r--src/glsl/tests/lower_jumps/.gitignore1
-rw-r--r--src/glsl/tests/lower_jumps/create_test_cases.py643
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_1.opt_test13
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_1.opt_test.expected5
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_2.opt_test15
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_2.opt_test.expected7
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_3.opt_test17
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_3.opt_test.expected8
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_4.opt_test15
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_4.opt_test.expected7
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_5.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_5.opt_test.expected7
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_breaks_6.opt_test29
-rw-r--r--src/glsl/tests/lower_jumps/lower_breaks_6.opt_test.expected29
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test21
-rw-r--r--src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test.expected20
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test28
-rw-r--r--src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test.expected25
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_1.opt_test12
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_1.opt_test.expected4
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_2.opt_test13
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_2.opt_test.expected5
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_3.opt_test20
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_3.opt_test.expected21
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_4.opt_test14
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_4.opt_test.expected16
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_main_false.opt_test17
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test.expected8
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_main_true.opt_test17
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test.expected13
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test.expected8
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test.expected13
-rwxr-xr-xsrc/glsl/tests/lower_jumps/lower_unified_returns.opt_test26
-rw-r--r--src/glsl/tests/lower_jumps/lower_unified_returns.opt_test.expected21
-rwxr-xr-xsrc/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test13
-rw-r--r--src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test.expected5
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test.expected8
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test.expected19
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test16
-rw-r--r--src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test.expected19
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test14
-rw-r--r--src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test.expected6
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test14
-rw-r--r--src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test.expected11
-rwxr-xr-xsrc/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test14
-rw-r--r--src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test.expected11
-rwxr-xr-xsrc/glsl/tests/optimization-test28
-rw-r--r--src/glsl/tests/sexps.py103
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
+