[Mesa-dev] [PATCH 4/4] glsl: Add unit tests for lower_jumps.cpp

Paul Berry stereotype441 at gmail.com
Fri Jul 8 15:30:27 PDT 2011


These tests invoke do_lower_jumps() in isolation (using the glsl_test
executable) and verify that it transforms the IR in the expected way.

The unit tests may be run from the top level directory using "make
check".

For reference, I've also checked in the Python script
create_test_cases.py, which was used to generate these tests.  It is
not necessary to run this script in order to run the tests.
---
 Makefile                                           |    6 +-
 src/glsl/tests/compare_ir                          |   59 ++
 src/glsl/tests/lower_jumps/.gitignore              |    1 +
 src/glsl/tests/lower_jumps/create_test_cases.py    |  643 ++++++++++++++++++++
 src/glsl/tests/lower_jumps/lower_breaks_1.opt_test |   13 +
 .../lower_jumps/lower_breaks_1.opt_test.expected   |    5 +
 src/glsl/tests/lower_jumps/lower_breaks_2.opt_test |   15 +
 .../lower_jumps/lower_breaks_2.opt_test.expected   |    7 +
 src/glsl/tests/lower_jumps/lower_breaks_3.opt_test |   17 +
 .../lower_jumps/lower_breaks_3.opt_test.expected   |    8 +
 src/glsl/tests/lower_jumps/lower_breaks_4.opt_test |   15 +
 .../lower_jumps/lower_breaks_4.opt_test.expected   |    7 +
 src/glsl/tests/lower_jumps/lower_breaks_5.opt_test |   16 +
 .../lower_jumps/lower_breaks_5.opt_test.expected   |    7 +
 src/glsl/tests/lower_jumps/lower_breaks_6.opt_test |   29 +
 .../lower_jumps/lower_breaks_6.opt_test.expected   |   29 +
 .../lower_guarded_conditional_break.opt_test       |   21 +
 ...wer_guarded_conditional_break.opt_test.expected |   20 +
 .../lower_jumps/lower_pulled_out_jump.opt_test     |   28 +
 .../lower_pulled_out_jump.opt_test.expected        |   25 +
 .../tests/lower_jumps/lower_returns_1.opt_test     |   12 +
 .../lower_jumps/lower_returns_1.opt_test.expected  |    4 +
 .../tests/lower_jumps/lower_returns_2.opt_test     |   13 +
 .../lower_jumps/lower_returns_2.opt_test.expected  |    5 +
 .../tests/lower_jumps/lower_returns_3.opt_test     |   20 +
 .../lower_jumps/lower_returns_3.opt_test.expected  |   21 +
 .../tests/lower_jumps/lower_returns_4.opt_test     |   14 +
 .../lower_jumps/lower_returns_4.opt_test.expected  |   16 +
 .../lower_jumps/lower_returns_main_false.opt_test  |   17 +
 .../lower_returns_main_false.opt_test.expected     |    8 +
 .../lower_jumps/lower_returns_main_true.opt_test   |   17 +
 .../lower_returns_main_true.opt_test.expected      |   13 +
 .../lower_jumps/lower_returns_sub_false.opt_test   |   16 +
 .../lower_returns_sub_false.opt_test.expected      |    8 +
 .../lower_jumps/lower_returns_sub_true.opt_test    |   16 +
 .../lower_returns_sub_true.opt_test.expected       |   13 +
 .../lower_jumps/lower_unified_returns.opt_test     |   26 +
 .../lower_unified_returns.opt_test.expected        |   21 +
 .../remove_continue_at_end_of_loop.opt_test        |   13 +
 ...emove_continue_at_end_of_loop.opt_test.expected |    5 +
 ..._non_void_at_end_of_loop_lower_nothing.opt_test |   16 +
 ..._at_end_of_loop_lower_nothing.opt_test.expected |    8 +
 ...n_non_void_at_end_of_loop_lower_return.opt_test |   16 +
 ...d_at_end_of_loop_lower_return.opt_test.expected |   19 +
 ..._at_end_of_loop_lower_return_and_break.opt_test |   16 +
 ...f_loop_lower_return_and_break.opt_test.expected |   19 +
 ...turn_void_at_end_of_loop_lower_nothing.opt_test |   14 +
 ..._at_end_of_loop_lower_nothing.opt_test.expected |    6 +
 ...eturn_void_at_end_of_loop_lower_return.opt_test |   14 +
 ...d_at_end_of_loop_lower_return.opt_test.expected |   11 +
 ..._at_end_of_loop_lower_return_and_break.opt_test |   14 +
 ...f_loop_lower_return_and_break.opt_test.expected |   11 +
 src/glsl/tests/optimization-test                   |   28 +
 src/glsl/tests/sexps.py                            |  103 ++++
 54 files changed, 1543 insertions(+), 1 deletions(-)
 create mode 100755 src/glsl/tests/compare_ir
 create mode 100644 src/glsl/tests/lower_jumps/.gitignore
 create mode 100644 src/glsl/tests/lower_jumps/create_test_cases.py
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_1.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_1.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_2.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_2.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_3.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_3.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_4.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_4.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_5.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_5.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_breaks_6.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_breaks_6.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_guarded_conditional_break.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_pulled_out_jump.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_1.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_1.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_2.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_2.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_3.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_3.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_4.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_4.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_main_false.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_main_true.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_sub_false.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_returns_sub_true.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/lower_unified_returns.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/lower_unified_returns.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/remove_continue_at_end_of_loop.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_nothing.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_non_void_at_end_of_loop_lower_return_and_break.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_nothing.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return.opt_test.expected
 create mode 100755 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test
 create mode 100644 src/glsl/tests/lower_jumps/return_void_at_end_of_loop_lower_return_and_break.opt_test.expected
 create mode 100755 src/glsl/tests/optimization-test
 create mode 100644 src/glsl/tests/sexps.py

diff --git a/Makefile b/Makefile
index 0a3deb8..f6c28fb 100644
--- a/Makefile
+++ b/Makefile
@@ -21,6 +21,10 @@ all: default
 doxygen:
 	cd doxygen && $(MAKE)
 
+check:
+	cd src/glsl/tests/ && ./optimization-test
+	@echo "All tests passed."
+
 clean:
 	- at touch $(TOP)/configs/current
 	- at for dir in $(SUBDIRS) ; do \
@@ -51,7 +55,7 @@ install:
 	done
 
 
-.PHONY: default doxygen clean realclean distclean install
+.PHONY: default doxygen clean realclean distclean install check
 
 # If there's no current configuration file
 $(TOP)/configs/current:
diff --git a/src/glsl/tests/compare_ir b/src/glsl/tests/compare_ir
new file mode 100755
index 0000000..a40fc81
--- /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 0000000..f47cb20
--- /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 0000000..fbc6f0a
--- /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 0000000..01ad708
--- /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 0000000..d4bb6fc
--- /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 0000000..0be22f9
--- /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 0000000..a4cb2d6
--- /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 0000000..4149360
--- /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 0000000..325f7b4
--- /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 0000000..70458bb
--- /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 0000000..a773545
--- /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 0000000..da9eef1
--- /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 0000000..0dd4a52
--- /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 0000000..9440dfe
--- /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 0000000..8222328
--- /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 0000000..379aa59
--- /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 0000000..7c6e73f
--- /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 0000000..15f3c41
--- /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 0000000..bf45c2c
--- /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 0000000..a1f895b
--- /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 0000000..7c3919c
--- /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 0000000..61673d4
--- /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 0000000..7777927
--- /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 0000000..9881e24
--- /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 0000000..d4835e9
--- /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 0000000..9f54c67
--- /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 0000000..b551a06
--- /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 0000000..5f97bfd
--- /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 0000000..e8b36f1
--- /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 0000000..59c7ba1
--- /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 0000000..e15a97d
--- /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 0000000..40e784e
--- /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 0000000..07db6e7
--- /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 0000000..9fe6b90
--- /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 0000000..3110980
--- /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 0000000..e716813
--- /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 0000000..271cd3b
--- /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 0000000..18efc37
--- /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 0000000..d2a02c6
--- /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 0000000..79c0e82
--- /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 0000000..2cf117a
--- /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 0000000..920d2ad
--- /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 0000000..0bab8f1
--- /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 0000000..99f1f86
--- /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 0000000..0bab8f1
--- /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 0000000..63487d3
--- /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 0000000..0bd8037
--- /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 0000000..523c92a
--- /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 0000000..53814ea
--- /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 0000000..22b5581
--- /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 0000000..53814ea
--- /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 0000000..0c130be
--- /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 0000000..a714af8
--- /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
+
-- 
1.7.6



More information about the mesa-dev mailing list