Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  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.