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