diff options
54 files changed, 1543 insertions, 1 deletions
@@ -21,6 +21,10 @@ all: default doxygen: cd doxygen && $(MAKE) +check: + cd src/glsl/tests/ && ./optimization-test + @echo "All tests passed." + clean: -@touch $(TOP)/configs/current -@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 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 + |