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