Home | History | Annotate | Download | only in lower_jumps
      1 # coding=utf-8
      2 #
      3 # Copyright  2011 Intel Corporation
      4 #
      5 # Permission is hereby granted, free of charge, to any person obtaining a
      6 # copy of this software and associated documentation files (the "Software"),
      7 # to deal in the Software without restriction, including without limitation
      8 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
      9 # and/or sell copies of the Software, and to permit persons to whom the
     10 # Software is furnished to do so, subject to the following conditions:
     11 #
     12 # The above copyright notice and this permission notice (including the next
     13 # paragraph) shall be included in all copies or substantial portions of the
     14 # Software.
     15 #
     16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     22 # DEALINGS IN THE SOFTWARE.
     23 
     24 import os
     25 import os.path
     26 import re
     27 import subprocess
     28 import sys
     29 
     30 sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # For access to sexps.py, which is in parent dir
     31 from sexps import *
     32 
     33 def make_test_case(f_name, ret_type, body):
     34     """Create a simple optimization test case consisting of a single
     35     function with the given name, return type, and body.
     36 
     37     Global declarations are automatically created for any undeclared
     38     variables that are referenced by the function.  All undeclared
     39     variables are assumed to be floats.
     40     """
     41     check_sexp(body)
     42     declarations = {}
     43     def make_declarations(sexp, already_declared = ()):
     44         if isinstance(sexp, list):
     45             if len(sexp) == 2 and sexp[0] == 'var_ref':
     46                 if sexp[1] not in already_declared:
     47                     declarations[sexp[1]] = [
     48                         'declare', ['in'], 'float', sexp[1]]
     49             elif len(sexp) == 4 and sexp[0] == 'assign':
     50                 assert sexp[2][0] == 'var_ref'
     51                 if sexp[2][1] not in already_declared:
     52                     declarations[sexp[2][1]] = [
     53                         'declare', ['out'], 'float', sexp[2][1]]
     54                 make_declarations(sexp[3], already_declared)
     55             else:
     56                 already_declared = set(already_declared)
     57                 for s in sexp:
     58                     if isinstance(s, list) and len(s) >= 4 and \
     59                             s[0] == 'declare':
     60                         already_declared.add(s[3])
     61                     else:
     62                         make_declarations(s, already_declared)
     63     make_declarations(body)
     64     return declarations.values() + \
     65         [['function', f_name, ['signature', ret_type, ['parameters'], body]]]
     66 
     67 
     68 # The following functions can be used to build expressions.
     69 
     70 def const_float(value):
     71     """Create an expression representing the given floating point value."""
     72     return ['constant', 'float', ['{0:.6f}'.format(value)]]
     73 
     74 def const_bool(value):
     75     """Create an expression representing the given boolean value.
     76 
     77     If value is not a boolean, it is converted to a boolean.  So, for
     78     instance, const_bool(1) is equivalent to const_bool(True).
     79     """
     80     return ['constant', 'bool', ['{0}'.format(1 if value else 0)]]
     81 
     82 def gt_zero(var_name):
     83     """Create Construct the expression var_name > 0"""
     84     return ['expression', 'bool', '>', ['var_ref', var_name], const_float(0)]
     85 
     86 
     87 # The following functions can be used to build complex control flow
     88 # statements.  All of these functions return statement lists (even
     89 # those which only create a single statement), so that statements can
     90 # be sequenced together using the '+' operator.
     91 
     92 def return_(value = None):
     93     """Create a return statement."""
     94     if value is not None:
     95         return [['return', value]]
     96     else:
     97         return [['return']]
     98 
     99 def break_():
    100     """Create a break statement."""
    101     return ['break']
    102 
    103 def continue_():
    104     """Create a continue statement."""
    105     return ['continue']
    106 
    107 def simple_if(var_name, then_statements, else_statements = None):
    108     """Create a statement of the form
    109 
    110     if (var_name > 0.0) {
    111        <then_statements>
    112     } else {
    113        <else_statements>
    114     }
    115 
    116     else_statements may be omitted.
    117     """
    118     if else_statements is None:
    119         else_statements = []
    120     check_sexp(then_statements)
    121     check_sexp(else_statements)
    122     return [['if', gt_zero(var_name), then_statements, else_statements]]
    123 
    124 def loop(statements):
    125     """Create a loop containing the given statements as its loop
    126     body.
    127     """
    128     check_sexp(statements)
    129     return [['loop', statements]]
    130 
    131 def declare_temp(var_type, var_name):
    132     """Create a declaration of the form
    133 
    134     (declare (temporary) <var_type> <var_name)
    135     """
    136     return [['declare', ['temporary'], var_type, var_name]]
    137 
    138 def assign_x(var_name, value):
    139     """Create a statement that assigns <value> to the variable
    140     <var_name>.  The assignment uses the mask (x).
    141     """
    142     check_sexp(value)
    143     return [['assign', ['x'], ['var_ref', var_name], value]]
    144 
    145 def complex_if(var_prefix, statements):
    146     """Create a statement of the form
    147 
    148     if (<var_prefix>a > 0.0) {
    149        if (<var_prefix>b > 0.0) {
    150           <statements>
    151        }
    152     }
    153 
    154     This is useful in testing jump lowering, because if <statements>
    155     ends in a jump, lower_jumps.cpp won't try to combine this
    156     construct with the code that follows it, as it might do for a
    157     simple if.
    158 
    159     All variables used in the if statement are prefixed with
    160     var_prefix.  This can be used to ensure uniqueness.
    161     """
    162     check_sexp(statements)
    163     return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements))
    164 
    165 def declare_execute_flag():
    166     """Create the statements that lower_jumps.cpp uses to declare and
    167     initialize the temporary boolean execute_flag.
    168     """
    169     return declare_temp('bool', 'execute_flag') + \
    170         assign_x('execute_flag', const_bool(True))
    171 
    172 def declare_return_flag():
    173     """Create the statements that lower_jumps.cpp uses to declare and
    174     initialize the temporary boolean return_flag.
    175     """
    176     return declare_temp('bool', 'return_flag') + \
    177         assign_x('return_flag', const_bool(False))
    178 
    179 def declare_return_value():
    180     """Create the statements that lower_jumps.cpp uses to declare and
    181     initialize the temporary variable return_value.  Assume that
    182     return_value is a float.
    183     """
    184     return declare_temp('float', 'return_value')
    185 
    186 def declare_break_flag():
    187     """Create the statements that lower_jumps.cpp uses to declare and
    188     initialize the temporary boolean break_flag.
    189     """
    190     return declare_temp('bool', 'break_flag') + \
    191         assign_x('break_flag', const_bool(False))
    192 
    193 def lowered_return_simple(value = None):
    194     """Create the statements that lower_jumps.cpp lowers a return
    195     statement to, in situations where it does not need to clear the
    196     execute flag.
    197     """
    198     if value:
    199         result = assign_x('return_value', value)
    200     else:
    201         result = []
    202     return result + assign_x('return_flag', const_bool(True))
    203 
    204 def lowered_return(value = None):
    205     """Create the statements that lower_jumps.cpp lowers a return
    206     statement to, in situations where it needs to clear the execute
    207     flag.
    208     """
    209     return lowered_return_simple(value) + \
    210         assign_x('execute_flag', const_bool(False))
    211 
    212 def lowered_continue():
    213     """Create the statement that lower_jumps.cpp lowers a continue
    214     statement to.
    215     """
    216     return assign_x('execute_flag', const_bool(False))
    217 
    218 def lowered_break_simple():
    219     """Create the statement that lower_jumps.cpp lowers a break
    220     statement to, in situations where it does not need to clear the
    221     execute flag.
    222     """
    223     return assign_x('break_flag', const_bool(True))
    224 
    225 def lowered_break():
    226     """Create the statement that lower_jumps.cpp lowers a break
    227     statement to, in situations where it needs to clear the execute
    228     flag.
    229     """
    230     return lowered_break_simple() + assign_x('execute_flag', const_bool(False))
    231 
    232 def if_execute_flag(statements):
    233     """Wrap statements in an if test so that they will only execute if
    234     execute_flag is True.
    235     """
    236     check_sexp(statements)
    237     return [['if', ['var_ref', 'execute_flag'], statements, []]]
    238 
    239 def if_return_flag(then_statements, else_statements):
    240     """Wrap statements in an if test with return_flag as the condition.
    241     """
    242     check_sexp(then_statements)
    243     check_sexp(else_statements)
    244     return [['if', ['var_ref', 'return_flag'], then_statements, else_statements]]
    245 
    246 def if_not_return_flag(statements):
    247     """Wrap statements in an if test so that they will only execute if
    248     return_flag is False.
    249     """
    250     check_sexp(statements)
    251     return [['if', ['var_ref', 'return_flag'], [], statements]]
    252 
    253 def final_return():
    254     """Create the return statement that lower_jumps.cpp places at the
    255     end of a function when lowering returns.
    256     """
    257     return [['return', ['var_ref', 'return_value']]]
    258 
    259 def final_break():
    260     """Create the conditional break statement that lower_jumps.cpp
    261     places at the end of a function when lowering breaks.
    262     """
    263     return [['if', ['var_ref', 'break_flag'], break_(), []]]
    264 
    265 def bash_quote(*args):
    266     """Quote the arguments appropriately so that bash will understand
    267     each argument as a single word.
    268     """
    269     def quote_word(word):
    270         for c in word:
    271             if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'):
    272                 break
    273         else:
    274             if not word:
    275                 return "''"
    276             return word
    277         return "'{0}'".format(word.replace("'", "'\"'\"'"))
    278     return ' '.join(quote_word(word) for word in args)
    279 
    280 def create_test_case(doc_string, input_sexp, expected_sexp, test_name,
    281                      pull_out_jumps=False, lower_sub_return=False,
    282                      lower_main_return=False, lower_continue=False,
    283                      lower_break=False):
    284     """Create a test case that verifies that do_lower_jumps transforms
    285     the given code in the expected way.
    286     """
    287     doc_lines = [line.strip() for line in doc_string.splitlines()]
    288     doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '')
    289     check_sexp(input_sexp)
    290     check_sexp(expected_sexp)
    291     input_str = sexp_to_string(sort_decls(input_sexp))
    292     expected_output = sexp_to_string(sort_decls(expected_sexp))
    293 
    294     optimization = (
    295         'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format(
    296             pull_out_jumps, lower_sub_return, lower_main_return,
    297             lower_continue, lower_break))
    298     args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization]
    299     test_file = '{0}.opt_test'.format(test_name)
    300     with open(test_file, 'w') as f:
    301         f.write('#!/usr/bin/env bash\n#\n# This file was generated by create_test_cases.py.\n#\n')
    302         f.write(doc_string)
    303         f.write('{0} <<EOF\n'.format(bash_quote(*args)))
    304         f.write('{0}\nEOF\n'.format(input_str))
    305     os.chmod(test_file, 0774)
    306     expected_file = '{0}.opt_test.expected'.format(test_name)
    307     with open(expected_file, 'w') as f:
    308         f.write('{0}\n'.format(expected_output))
    309 
    310 def test_lower_returns_main():
    311     doc_string = """Test that do_lower_jumps respects the lower_main_return
    312     flag in deciding whether to lower returns in the main
    313     function.
    314     """
    315     input_sexp = make_test_case('main', 'void', (
    316             complex_if('', return_())
    317             ))
    318     expected_sexp = make_test_case('main', 'void', (
    319             declare_execute_flag() +
    320             declare_return_flag() +
    321             complex_if('', lowered_return())
    322             ))
    323     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_main_true',
    324                      lower_main_return=True)
    325     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_main_false',
    326                      lower_main_return=False)
    327 
    328 def test_lower_returns_sub():
    329     doc_string = """Test that do_lower_jumps respects the lower_sub_return flag
    330     in deciding whether to lower returns in subroutines.
    331     """
    332     input_sexp = make_test_case('sub', 'void', (
    333             complex_if('', return_())
    334             ))
    335     expected_sexp = make_test_case('sub', 'void', (
    336             declare_execute_flag() +
    337             declare_return_flag() +
    338             complex_if('', lowered_return())
    339             ))
    340     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_sub_true',
    341                      lower_sub_return=True)
    342     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_sub_false',
    343                      lower_sub_return=False)
    344 
    345 def test_lower_returns_1():
    346     doc_string = """Test that a void return at the end of a function is
    347     eliminated.
    348     """
    349     input_sexp = make_test_case('main', 'void', (
    350             assign_x('a', const_float(1)) +
    351             return_()
    352             ))
    353     expected_sexp = make_test_case('main', 'void', (
    354             assign_x('a', const_float(1))
    355             ))
    356     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_1',
    357                      lower_main_return=True)
    358 
    359 def test_lower_returns_2():
    360     doc_string = """Test that lowering is not performed on a non-void return at
    361     the end of subroutine.
    362     """
    363     input_sexp = make_test_case('sub', 'float', (
    364             assign_x('a', const_float(1)) +
    365             return_(const_float(1))
    366             ))
    367     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_2',
    368                      lower_sub_return=True)
    369 
    370 def test_lower_returns_3():
    371     doc_string = """Test lowering of returns when there is one nested inside a
    372     complex structure of ifs, and one at the end of a function.
    373 
    374     In this case, the latter return needs to be lowered because it
    375     will not be at the end of the function once the final return
    376     is inserted.
    377     """
    378     input_sexp = make_test_case('sub', 'float', (
    379             complex_if('', return_(const_float(1))) +
    380             return_(const_float(2))
    381             ))
    382     expected_sexp = make_test_case('sub', 'float', (
    383             declare_execute_flag() +
    384             declare_return_value() +
    385             declare_return_flag() +
    386             complex_if('', lowered_return(const_float(1))) +
    387             if_execute_flag(lowered_return(const_float(2))) +
    388             final_return()
    389             ))
    390     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_3',
    391                      lower_sub_return=True)
    392 
    393 def test_lower_returns_4():
    394     doc_string = """Test that returns are properly lowered when they occur in
    395     both branches of an if-statement.
    396     """
    397     input_sexp = make_test_case('sub', 'float', (
    398             simple_if('a', return_(const_float(1)),
    399                       return_(const_float(2)))
    400             ))
    401     expected_sexp = make_test_case('sub', 'float', (
    402             declare_execute_flag() +
    403             declare_return_value() +
    404             declare_return_flag() +
    405             simple_if('a', lowered_return(const_float(1)),
    406                       lowered_return(const_float(2))) +
    407             final_return()
    408             ))
    409     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_4',
    410                      lower_sub_return=True)
    411 
    412 def test_lower_unified_returns():
    413     doc_string = """If both branches of an if statement end in a return, and
    414     pull_out_jumps is True, then those returns should be lifted
    415     outside the if and then properly lowered.
    416 
    417     Verify that this lowering occurs during the same pass as the
    418     lowering of other returns by checking that extra temporary
    419     variables aren't generated.
    420     """
    421     input_sexp = make_test_case('main', 'void', (
    422             complex_if('a', return_()) +
    423             simple_if('b', simple_if('c', return_(), return_()))
    424             ))
    425     expected_sexp = make_test_case('main', 'void', (
    426             declare_execute_flag() +
    427             declare_return_flag() +
    428             complex_if('a', lowered_return()) +
    429             if_execute_flag(simple_if('b', (simple_if('c', [], []) +
    430                                             lowered_return())))
    431             ))
    432     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_unified_returns',
    433                      lower_main_return=True, pull_out_jumps=True)
    434 
    435 def test_lower_pulled_out_jump():
    436     doc_string = """If one branch of an if ends in a jump, and control cannot
    437     fall out the bottom of the other branch, and pull_out_jumps is
    438     True, then the jump is lifted outside the if.
    439 
    440     Verify that this lowering occurs during the same pass as the
    441     lowering of other jumps by checking that extra temporary
    442     variables aren't generated.
    443     """
    444     input_sexp = make_test_case('main', 'void', (
    445             complex_if('a', return_()) +
    446             loop(simple_if('b', simple_if('c', break_(), continue_()),
    447                            return_())) +
    448             assign_x('d', const_float(1))
    449             ))
    450     # Note: optimization produces two other effects: the break
    451     # gets lifted out of the if statements, and the code after the
    452     # loop gets guarded so that it only executes if the return
    453     # flag is clear.
    454     expected_sexp = make_test_case('main', 'void', (
    455             declare_execute_flag() +
    456             declare_return_flag() +
    457             complex_if('a', lowered_return()) +
    458             if_execute_flag(
    459                 loop(simple_if('b', simple_if('c', [], continue_()),
    460                                lowered_return_simple()) +
    461                      break_()) +
    462 
    463                 if_return_flag(assign_x('return_flag', const_bool(1)) +
    464                                assign_x('execute_flag', const_bool(0)),
    465                                assign_x('d', const_float(1))))
    466             ))
    467     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_pulled_out_jump',
    468                      lower_main_return=True, pull_out_jumps=True)
    469 
    470 def test_lower_breaks_1():
    471     doc_string = """If a loop contains an unconditional break at the bottom of
    472     it, it should not be lowered."""
    473     input_sexp = make_test_case('main', 'void', (
    474             loop(assign_x('a', const_float(1)) +
    475                  break_())
    476             ))
    477     expected_sexp = input_sexp
    478     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_1', lower_break=True)
    479 
    480 def test_lower_breaks_2():
    481     doc_string = """If a loop contains a conditional break at the bottom of it,
    482     it should not be lowered if it is in the then-clause.
    483     """
    484     input_sexp = make_test_case('main', 'void', (
    485             loop(assign_x('a', const_float(1)) +
    486                  simple_if('b', break_()))
    487             ))
    488     expected_sexp = input_sexp
    489     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_2', lower_break=True)
    490 
    491 def test_lower_breaks_3():
    492     doc_string = """If a loop contains a conditional break at the bottom of it,
    493     it should not be lowered if it is in the then-clause, even if
    494     there are statements preceding the break.
    495     """
    496     input_sexp = make_test_case('main', 'void', (
    497             loop(assign_x('a', const_float(1)) +
    498                  simple_if('b', (assign_x('c', const_float(1)) +
    499                                  break_())))
    500             ))
    501     expected_sexp = input_sexp
    502     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_3', lower_break=True)
    503 
    504 def test_lower_breaks_4():
    505     doc_string = """If a loop contains a conditional break at the bottom of it,
    506     it should not be lowered if it is in the else-clause.
    507     """
    508     input_sexp = make_test_case('main', 'void', (
    509             loop(assign_x('a', const_float(1)) +
    510                  simple_if('b', [], break_()))
    511             ))
    512     expected_sexp = input_sexp
    513     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_4', lower_break=True)
    514 
    515 def test_lower_breaks_5():
    516     doc_string = """If a loop contains a conditional break at the bottom of it,
    517     it should not be lowered if it is in the else-clause, even if
    518     there are statements preceding the break.
    519     """
    520     input_sexp = make_test_case('main', 'void', (
    521             loop(assign_x('a', const_float(1)) +
    522                  simple_if('b', [], (assign_x('c', const_float(1)) +
    523                                      break_())))
    524             ))
    525     expected_sexp = input_sexp
    526     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_5', lower_break=True)
    527 
    528 def test_lower_breaks_6():
    529     doc_string = """If a loop contains conditional breaks and continues, and
    530     ends in an unconditional break, then the unconditional break
    531     needs to be lowered, because it will no longer be at the end
    532     of the loop after the final break is added.
    533     """
    534     input_sexp = make_test_case('main', 'void', (
    535             loop(simple_if('a', (complex_if('b', continue_()) +
    536                                  complex_if('c', break_()))) +
    537                  break_())
    538             ))
    539     expected_sexp = make_test_case('main', 'void', (
    540             declare_break_flag() +
    541             loop(declare_execute_flag() +
    542                  simple_if(
    543                     'a',
    544                     (complex_if('b', lowered_continue()) +
    545                      if_execute_flag(
    546                             complex_if('c', lowered_break())))) +
    547                  if_execute_flag(lowered_break_simple()) +
    548                  final_break())
    549             ))
    550     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_6',
    551                      lower_break=True, lower_continue=True)
    552 
    553 def test_lower_guarded_conditional_break():
    554     doc_string = """Normally a conditional break at the end of a loop isn't
    555     lowered, however if the conditional break gets placed inside
    556     an if(execute_flag) because of earlier lowering of continues,
    557     then the break needs to be lowered.
    558     """
    559     input_sexp = make_test_case('main', 'void', (
    560             loop(complex_if('a', continue_()) +
    561                  simple_if('b', break_()))
    562             ))
    563     expected_sexp = make_test_case('main', 'void', (
    564             declare_break_flag() +
    565             loop(declare_execute_flag() +
    566                  complex_if('a', lowered_continue()) +
    567                  if_execute_flag(simple_if('b', lowered_break())) +
    568                  final_break())
    569             ))
    570     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_guarded_conditional_break',
    571                      lower_break=True, lower_continue=True)
    572 
    573 def test_remove_continue_at_end_of_loop():
    574     doc_string = """Test that a redundant continue-statement at the end of a
    575     loop is removed.
    576     """
    577     input_sexp = make_test_case('main', 'void', (
    578             loop(assign_x('a', const_float(1)) +
    579                  continue_())
    580             ))
    581     expected_sexp = make_test_case('main', 'void', (
    582             loop(assign_x('a', const_float(1)))
    583             ))
    584     create_test_case(doc_string, input_sexp, expected_sexp, 'remove_continue_at_end_of_loop')
    585 
    586 def test_lower_return_void_at_end_of_loop():
    587     doc_string = """Test that a return of void at the end of a loop is properly
    588     lowered.
    589     """
    590     input_sexp = make_test_case('main', 'void', (
    591             loop(assign_x('a', const_float(1)) +
    592                  return_()) +
    593             assign_x('b', const_float(2))
    594             ))
    595     expected_sexp = make_test_case('main', 'void', (
    596             declare_execute_flag() +
    597             declare_return_flag() +
    598             loop(assign_x('a', const_float(1)) +
    599                  lowered_return_simple() +
    600                  break_()) +
    601             if_return_flag(assign_x('return_flag', const_bool(1)) +
    602                            assign_x('execute_flag', const_bool(0)),
    603                            assign_x('b', const_float(2)))
    604             ))
    605     create_test_case(doc_string, input_sexp, input_sexp, 'return_void_at_end_of_loop_lower_nothing')
    606     create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return',
    607                      lower_main_return=True)
    608     create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return_and_break',
    609                      lower_main_return=True, lower_break=True)
    610 
    611 def test_lower_return_non_void_at_end_of_loop():
    612     doc_string = """Test that a non-void return at the end of a loop is
    613     properly lowered.
    614     """
    615     input_sexp = make_test_case('sub', 'float', (
    616             loop(assign_x('a', const_float(1)) +
    617                  return_(const_float(2))) +
    618             assign_x('b', const_float(3)) +
    619             return_(const_float(4))
    620             ))
    621     expected_sexp = make_test_case('sub', 'float', (
    622             declare_execute_flag() +
    623             declare_return_value() +
    624             declare_return_flag() +
    625             loop(assign_x('a', const_float(1)) +
    626                  lowered_return_simple(const_float(2)) +
    627                  break_()) +
    628             if_not_return_flag(assign_x('b', const_float(3)) +
    629                                lowered_return(const_float(4))) +
    630             final_return()
    631             ))
    632     create_test_case(doc_string, input_sexp, input_sexp, 'return_non_void_at_end_of_loop_lower_nothing')
    633     create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return',
    634                      lower_sub_return=True)
    635     create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return_and_break',
    636                      lower_sub_return=True, lower_break=True)
    637 
    638 if __name__ == '__main__':
    639     test_lower_returns_main()
    640     test_lower_returns_sub()
    641     test_lower_returns_1()
    642     test_lower_returns_2()
    643     test_lower_returns_3()
    644     test_lower_returns_4()
    645     test_lower_unified_returns()
    646     test_lower_pulled_out_jump()
    647     test_lower_breaks_1()
    648     test_lower_breaks_2()
    649     test_lower_breaks_3()
    650     test_lower_breaks_4()
    651     test_lower_breaks_5()
    652     test_lower_breaks_6()
    653     test_lower_guarded_conditional_break()
    654     test_remove_continue_at_end_of_loop()
    655     test_lower_return_void_at_end_of_loop()
    656     test_lower_return_non_void_at_end_of_loop()
    657