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_not_return_flag(statements):
    240     """Wrap statements in an if test so that they will only execute if
    241     return_flag is False.
    242     """
    243     check_sexp(statements)
    244     return [['if', ['var_ref', 'return_flag'], [], statements]]
    245 
    246 def final_return():
    247     """Create the return statement that lower_jumps.cpp places at the
    248     end of a function when lowering returns.
    249     """
    250     return [['return', ['var_ref', 'return_value']]]
    251 
    252 def final_break():
    253     """Create the conditional break statement that lower_jumps.cpp
    254     places at the end of a function when lowering breaks.
    255     """
    256     return [['if', ['var_ref', 'break_flag'], break_(), []]]
    257 
    258 def bash_quote(*args):
    259     """Quote the arguments appropriately so that bash will understand
    260     each argument as a single word.
    261     """
    262     def quote_word(word):
    263         for c in word:
    264             if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'):
    265                 break
    266         else:
    267             if not word:
    268                 return "''"
    269             return word
    270         return "'{0}'".format(word.replace("'", "'\"'\"'"))
    271     return ' '.join(quote_word(word) for word in args)
    272 
    273 def create_test_case(doc_string, input_sexp, expected_sexp, test_name,
    274                      pull_out_jumps=False, lower_sub_return=False,
    275                      lower_main_return=False, lower_continue=False,
    276                      lower_break=False):
    277     """Create a test case that verifies that do_lower_jumps transforms
    278     the given code in the expected way.
    279     """
    280     doc_lines = [line.strip() for line in doc_string.splitlines()]
    281     doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '')
    282     check_sexp(input_sexp)
    283     check_sexp(expected_sexp)
    284     input_str = sexp_to_string(sort_decls(input_sexp))
    285     expected_output = sexp_to_string(sort_decls(expected_sexp))
    286 
    287     optimization = (
    288         'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format(
    289             pull_out_jumps, lower_sub_return, lower_main_return,
    290             lower_continue, lower_break))
    291     args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization]
    292     test_file = '{0}.opt_test'.format(test_name)
    293     with open(test_file, 'w') as f:
    294         f.write('#!/bin/bash\n#\n# This file was generated by create_test_cases.py.\n#\n')
    295         f.write(doc_string)
    296         f.write('{0} <<EOF\n'.format(bash_quote(*args)))
    297         f.write('{0}\nEOF\n'.format(input_str))
    298     os.chmod(test_file, 0774)
    299     expected_file = '{0}.opt_test.expected'.format(test_name)
    300     with open(expected_file, 'w') as f:
    301         f.write('{0}\n'.format(expected_output))
    302 
    303 def test_lower_returns_main():
    304     doc_string = """Test that do_lower_jumps respects the lower_main_return
    305     flag in deciding whether to lower returns in the main
    306     function.
    307     """
    308     input_sexp = make_test_case('main', 'void', (
    309             complex_if('', return_())
    310             ))
    311     expected_sexp = make_test_case('main', 'void', (
    312             declare_execute_flag() +
    313             declare_return_flag() +
    314             complex_if('', lowered_return())
    315             ))
    316     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_main_true',
    317                      lower_main_return=True)
    318     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_main_false',
    319                      lower_main_return=False)
    320 
    321 def test_lower_returns_sub():
    322     doc_string = """Test that do_lower_jumps respects the lower_sub_return flag
    323     in deciding whether to lower returns in subroutines.
    324     """
    325     input_sexp = make_test_case('sub', 'void', (
    326             complex_if('', return_())
    327             ))
    328     expected_sexp = make_test_case('sub', 'void', (
    329             declare_execute_flag() +
    330             declare_return_flag() +
    331             complex_if('', lowered_return())
    332             ))
    333     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_sub_true',
    334                      lower_sub_return=True)
    335     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_sub_false',
    336                      lower_sub_return=False)
    337 
    338 def test_lower_returns_1():
    339     doc_string = """Test that a void return at the end of a function is
    340     eliminated.
    341     """
    342     input_sexp = make_test_case('main', 'void', (
    343             assign_x('a', const_float(1)) +
    344             return_()
    345             ))
    346     expected_sexp = make_test_case('main', 'void', (
    347             assign_x('a', const_float(1))
    348             ))
    349     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_1',
    350                      lower_main_return=True)
    351 
    352 def test_lower_returns_2():
    353     doc_string = """Test that lowering is not performed on a non-void return at
    354     the end of subroutine.
    355     """
    356     input_sexp = make_test_case('sub', 'float', (
    357             assign_x('a', const_float(1)) +
    358             return_(const_float(1))
    359             ))
    360     create_test_case(doc_string, input_sexp, input_sexp, 'lower_returns_2',
    361                      lower_sub_return=True)
    362 
    363 def test_lower_returns_3():
    364     doc_string = """Test lowering of returns when there is one nested inside a
    365     complex structure of ifs, and one at the end of a function.
    366 
    367     In this case, the latter return needs to be lowered because it
    368     will not be at the end of the function once the final return
    369     is inserted.
    370     """
    371     input_sexp = make_test_case('sub', 'float', (
    372             complex_if('', return_(const_float(1))) +
    373             return_(const_float(2))
    374             ))
    375     expected_sexp = make_test_case('sub', 'float', (
    376             declare_execute_flag() +
    377             declare_return_value() +
    378             declare_return_flag() +
    379             complex_if('', lowered_return(const_float(1))) +
    380             if_execute_flag(lowered_return(const_float(2))) +
    381             final_return()
    382             ))
    383     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_3',
    384                      lower_sub_return=True)
    385 
    386 def test_lower_returns_4():
    387     doc_string = """Test that returns are properly lowered when they occur in
    388     both branches of an if-statement.
    389     """
    390     input_sexp = make_test_case('sub', 'float', (
    391             simple_if('a', return_(const_float(1)),
    392                       return_(const_float(2)))
    393             ))
    394     expected_sexp = make_test_case('sub', 'float', (
    395             declare_execute_flag() +
    396             declare_return_value() +
    397             declare_return_flag() +
    398             simple_if('a', lowered_return(const_float(1)),
    399                       lowered_return(const_float(2))) +
    400             final_return()
    401             ))
    402     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_returns_4',
    403                      lower_sub_return=True)
    404 
    405 def test_lower_unified_returns():
    406     doc_string = """If both branches of an if statement end in a return, and
    407     pull_out_jumps is True, then those returns should be lifted
    408     outside the if and then properly lowered.
    409 
    410     Verify that this lowering occurs during the same pass as the
    411     lowering of other returns by checking that extra temporary
    412     variables aren't generated.
    413     """
    414     input_sexp = make_test_case('main', 'void', (
    415             complex_if('a', return_()) +
    416             simple_if('b', simple_if('c', return_(), return_()))
    417             ))
    418     expected_sexp = make_test_case('main', 'void', (
    419             declare_execute_flag() +
    420             declare_return_flag() +
    421             complex_if('a', lowered_return()) +
    422             if_execute_flag(simple_if('b', (simple_if('c', [], []) +
    423                                             lowered_return())))
    424             ))
    425     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_unified_returns',
    426                      lower_main_return=True, pull_out_jumps=True)
    427 
    428 def test_lower_pulled_out_jump():
    429     doc_string = """If one branch of an if ends in a jump, and control cannot
    430     fall out the bottom of the other branch, and pull_out_jumps is
    431     True, then the jump is lifted outside the if.
    432 
    433     Verify that this lowering occurs during the same pass as the
    434     lowering of other jumps by checking that extra temporary
    435     variables aren't generated.
    436     """
    437     input_sexp = make_test_case('main', 'void', (
    438             complex_if('a', return_()) +
    439             loop(simple_if('b', simple_if('c', break_(), continue_()),
    440                            return_())) +
    441             assign_x('d', const_float(1))
    442             ))
    443     # Note: optimization produces two other effects: the break
    444     # gets lifted out of the if statements, and the code after the
    445     # loop gets guarded so that it only executes if the return
    446     # flag is clear.
    447     expected_sexp = make_test_case('main', 'void', (
    448             declare_execute_flag() +
    449             declare_return_flag() +
    450             complex_if('a', lowered_return()) +
    451             if_execute_flag(
    452                 loop(simple_if('b', simple_if('c', [], continue_()),
    453                                lowered_return_simple()) +
    454                      break_()) +
    455                 if_not_return_flag(assign_x('d', const_float(1))))
    456             ))
    457     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_pulled_out_jump',
    458                      lower_main_return=True, pull_out_jumps=True)
    459 
    460 def test_lower_breaks_1():
    461     doc_string = """If a loop contains an unconditional break at the bottom of
    462     it, it should not be lowered."""
    463     input_sexp = make_test_case('main', 'void', (
    464             loop(assign_x('a', const_float(1)) +
    465                  break_())
    466             ))
    467     expected_sexp = input_sexp
    468     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_1', lower_break=True)
    469 
    470 def test_lower_breaks_2():
    471     doc_string = """If a loop contains a conditional break at the bottom of it,
    472     it should not be lowered if it is in the then-clause.
    473     """
    474     input_sexp = make_test_case('main', 'void', (
    475             loop(assign_x('a', const_float(1)) +
    476                  simple_if('b', break_()))
    477             ))
    478     expected_sexp = input_sexp
    479     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_2', lower_break=True)
    480 
    481 def test_lower_breaks_3():
    482     doc_string = """If a loop contains a conditional break at the bottom of it,
    483     it should not be lowered if it is in the then-clause, even if
    484     there are statements preceding the break.
    485     """
    486     input_sexp = make_test_case('main', 'void', (
    487             loop(assign_x('a', const_float(1)) +
    488                  simple_if('b', (assign_x('c', const_float(1)) +
    489                                  break_())))
    490             ))
    491     expected_sexp = input_sexp
    492     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_3', lower_break=True)
    493 
    494 def test_lower_breaks_4():
    495     doc_string = """If a loop contains a conditional break at the bottom of it,
    496     it should not be lowered if it is in the else-clause.
    497     """
    498     input_sexp = make_test_case('main', 'void', (
    499             loop(assign_x('a', const_float(1)) +
    500                  simple_if('b', [], break_()))
    501             ))
    502     expected_sexp = input_sexp
    503     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_4', lower_break=True)
    504 
    505 def test_lower_breaks_5():
    506     doc_string = """If a loop contains a conditional break at the bottom of it,
    507     it should not be lowered if it is in the else-clause, even if
    508     there are statements preceding the break.
    509     """
    510     input_sexp = make_test_case('main', 'void', (
    511             loop(assign_x('a', const_float(1)) +
    512                  simple_if('b', [], (assign_x('c', const_float(1)) +
    513                                      break_())))
    514             ))
    515     expected_sexp = input_sexp
    516     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_5', lower_break=True)
    517 
    518 def test_lower_breaks_6():
    519     doc_string = """If a loop contains conditional breaks and continues, and
    520     ends in an unconditional break, then the unconditional break
    521     needs to be lowered, because it will no longer be at the end
    522     of the loop after the final break is added.
    523     """
    524     input_sexp = make_test_case('main', 'void', (
    525             loop(simple_if('a', (complex_if('b', continue_()) +
    526                                  complex_if('c', break_()))) +
    527                  break_())
    528             ))
    529     expected_sexp = make_test_case('main', 'void', (
    530             declare_break_flag() +
    531             loop(declare_execute_flag() +
    532                  simple_if(
    533                     'a',
    534                     (complex_if('b', lowered_continue()) +
    535                      if_execute_flag(
    536                             complex_if('c', lowered_break())))) +
    537                  if_execute_flag(lowered_break_simple()) +
    538                  final_break())
    539             ))
    540     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_breaks_6',
    541                      lower_break=True, lower_continue=True)
    542 
    543 def test_lower_guarded_conditional_break():
    544     doc_string = """Normally a conditional break at the end of a loop isn't
    545     lowered, however if the conditional break gets placed inside
    546     an if(execute_flag) because of earlier lowering of continues,
    547     then the break needs to be lowered.
    548     """
    549     input_sexp = make_test_case('main', 'void', (
    550             loop(complex_if('a', continue_()) +
    551                  simple_if('b', break_()))
    552             ))
    553     expected_sexp = make_test_case('main', 'void', (
    554             declare_break_flag() +
    555             loop(declare_execute_flag() +
    556                  complex_if('a', lowered_continue()) +
    557                  if_execute_flag(simple_if('b', lowered_break())) +
    558                  final_break())
    559             ))
    560     create_test_case(doc_string, input_sexp, expected_sexp, 'lower_guarded_conditional_break',
    561                      lower_break=True, lower_continue=True)
    562 
    563 def test_remove_continue_at_end_of_loop():
    564     doc_string = """Test that a redundant continue-statement at the end of a
    565     loop is removed.
    566     """
    567     input_sexp = make_test_case('main', 'void', (
    568             loop(assign_x('a', const_float(1)) +
    569                  continue_())
    570             ))
    571     expected_sexp = make_test_case('main', 'void', (
    572             loop(assign_x('a', const_float(1)))
    573             ))
    574     create_test_case(doc_string, input_sexp, expected_sexp, 'remove_continue_at_end_of_loop')
    575 
    576 def test_lower_return_void_at_end_of_loop():
    577     doc_string = """Test that a return of void at the end of a loop is properly
    578     lowered.
    579     """
    580     input_sexp = make_test_case('main', 'void', (
    581             loop(assign_x('a', const_float(1)) +
    582                  return_()) +
    583             assign_x('b', const_float(2))
    584             ))
    585     expected_sexp = make_test_case('main', 'void', (
    586             declare_return_flag() +
    587             loop(assign_x('a', const_float(1)) +
    588                  lowered_return_simple() +
    589                  break_()) +
    590             if_not_return_flag(assign_x('b', const_float(2)))
    591             ))
    592     create_test_case(doc_string, input_sexp, input_sexp, 'return_void_at_end_of_loop_lower_nothing')
    593     create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return',
    594                      lower_main_return=True)
    595     create_test_case(doc_string, input_sexp, expected_sexp, 'return_void_at_end_of_loop_lower_return_and_break',
    596                      lower_main_return=True, lower_break=True)
    597 
    598 def test_lower_return_non_void_at_end_of_loop():
    599     doc_string = """Test that a non-void return at the end of a loop is
    600     properly lowered.
    601     """
    602     input_sexp = make_test_case('sub', 'float', (
    603             loop(assign_x('a', const_float(1)) +
    604                  return_(const_float(2))) +
    605             assign_x('b', const_float(3)) +
    606             return_(const_float(4))
    607             ))
    608     expected_sexp = make_test_case('sub', 'float', (
    609             declare_execute_flag() +
    610             declare_return_value() +
    611             declare_return_flag() +
    612             loop(assign_x('a', const_float(1)) +
    613                  lowered_return_simple(const_float(2)) +
    614                  break_()) +
    615             if_not_return_flag(assign_x('b', const_float(3)) +
    616                                lowered_return(const_float(4))) +
    617             final_return()
    618             ))
    619     create_test_case(doc_string, input_sexp, input_sexp, 'return_non_void_at_end_of_loop_lower_nothing')
    620     create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return',
    621                      lower_sub_return=True)
    622     create_test_case(doc_string, input_sexp, expected_sexp, 'return_non_void_at_end_of_loop_lower_return_and_break',
    623                      lower_sub_return=True, lower_break=True)
    624 
    625 if __name__ == '__main__':
    626     test_lower_returns_main()
    627     test_lower_returns_sub()
    628     test_lower_returns_1()
    629     test_lower_returns_2()
    630     test_lower_returns_3()
    631     test_lower_returns_4()
    632     test_lower_unified_returns()
    633     test_lower_pulled_out_jump()
    634     test_lower_breaks_1()
    635     test_lower_breaks_2()
    636     test_lower_breaks_3()
    637     test_lower_breaks_4()
    638     test_lower_breaks_5()
    639     test_lower_breaks_6()
    640     test_lower_guarded_conditional_break()
    641     test_remove_continue_at_end_of_loop()
    642     test_lower_return_void_at_end_of_loop()
    643     test_lower_return_non_void_at_end_of_loop()
    644