3 # Copyright © 2011, 2018 Intel Corporation
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:
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
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.
26 def make_test_case(f_name
, ret_type
, body
):
27 """Create a simple optimization test case consisting of a single
28 function with the given name, return type, and body.
30 Global declarations are automatically created for any undeclared
31 variables that are referenced by the function. All undeclared
32 variables are assumed to be floats.
36 def make_declarations(sexp
, already_declared
= ()):
37 if isinstance(sexp
, list):
38 if len(sexp
) == 2 and sexp
[0] == 'var_ref':
39 if sexp
[1] not in already_declared
:
40 declarations
[sexp
[1]] = [
41 'declare', ['in'], 'float', sexp
[1]]
42 elif len(sexp
) == 4 and sexp
[0] == 'assign':
43 assert sexp
[2][0] == 'var_ref'
44 if sexp
[2][1] not in already_declared
:
45 declarations
[sexp
[2][1]] = [
46 'declare', ['out'], 'float', sexp
[2][1]]
47 make_declarations(sexp
[3], already_declared
)
49 already_declared
= set(already_declared
)
51 if isinstance(s
, list) and len(s
) >= 4 and \
53 already_declared
.add(s
[3])
55 make_declarations(s
, already_declared
)
56 make_declarations(body
)
57 return list(declarations
.values()) + \
58 [['function', f_name
, ['signature', ret_type
, ['parameters'], body
]]]
61 # The following functions can be used to build expressions.
63 def const_float(value
):
64 """Create an expression representing the given floating point value."""
65 return ['constant', 'float', ['{0:.6f}'.format(value
)]]
67 def const_bool(value
):
68 """Create an expression representing the given boolean value.
70 If value is not a boolean, it is converted to a boolean. So, for
71 instance, const_bool(1) is equivalent to const_bool(True).
73 return ['constant', 'bool', ['{0}'.format(1 if value
else 0)]]
75 def gt_zero(var_name
):
76 """Create Construct the expression var_name > 0"""
77 return ['expression', 'bool', '<', const_float(0), ['var_ref', var_name
]]
80 # The following functions can be used to build complex control flow
81 # statements. All of these functions return statement lists (even
82 # those which only create a single statement), so that statements can
83 # be sequenced together using the '+' operator.
85 def return_(value
= None):
86 """Create a return statement."""
88 return [['return', value
]]
93 """Create a break statement."""
97 """Create a continue statement."""
100 def simple_if(var_name
, then_statements
, else_statements
= None):
101 """Create a statement of the form
103 if (var_name > 0.0) {
109 else_statements may be omitted.
111 if else_statements
is None:
113 check_sexp(then_statements
)
114 check_sexp(else_statements
)
115 return [['if', gt_zero(var_name
), then_statements
, else_statements
]]
117 def loop(statements
):
118 """Create a loop containing the given statements as its loop
121 check_sexp(statements
)
122 return [['loop', statements
]]
124 def declare_temp(var_type
, var_name
):
125 """Create a declaration of the form
127 (declare (temporary) <var_type> <var_name)
129 return [['declare', ['temporary'], var_type
, var_name
]]
131 def assign_x(var_name
, value
):
132 """Create a statement that assigns <value> to the variable
133 <var_name>. The assignment uses the mask (x).
136 return [['assign', ['x'], ['var_ref', var_name
], value
]]
138 def complex_if(var_prefix
, statements
):
139 """Create a statement of the form
141 if (<var_prefix>a > 0.0) {
142 if (<var_prefix>b > 0.0) {
147 This is useful in testing jump lowering, because if <statements>
148 ends in a jump, lower_jumps.cpp won't try to combine this
149 construct with the code that follows it, as it might do for a
152 All variables used in the if statement are prefixed with
153 var_prefix. This can be used to ensure uniqueness.
155 check_sexp(statements
)
156 return simple_if(var_prefix
+ 'a', simple_if(var_prefix
+ 'b', statements
))
158 def declare_execute_flag():
159 """Create the statements that lower_jumps.cpp uses to declare and
160 initialize the temporary boolean execute_flag.
162 return declare_temp('bool', 'execute_flag') + \
163 assign_x('execute_flag', const_bool(True))
165 def declare_return_flag():
166 """Create the statements that lower_jumps.cpp uses to declare and
167 initialize the temporary boolean return_flag.
169 return declare_temp('bool', 'return_flag') + \
170 assign_x('return_flag', const_bool(False))
172 def declare_return_value():
173 """Create the statements that lower_jumps.cpp uses to declare and
174 initialize the temporary variable return_value. Assume that
175 return_value is a float.
177 return declare_temp('float', 'return_value')
179 def declare_break_flag():
180 """Create the statements that lower_jumps.cpp uses to declare and
181 initialize the temporary boolean break_flag.
183 return declare_temp('bool', 'break_flag') + \
184 assign_x('break_flag', const_bool(False))
186 def lowered_return_simple(value
= None):
187 """Create the statements that lower_jumps.cpp lowers a return
188 statement to, in situations where it does not need to clear the
192 result
= assign_x('return_value', value
)
195 return result
+ assign_x('return_flag', const_bool(True))
197 def lowered_return(value
= None):
198 """Create the statements that lower_jumps.cpp lowers a return
199 statement to, in situations where it needs to clear the execute
202 return lowered_return_simple(value
) + \
203 assign_x('execute_flag', const_bool(False))
205 def lowered_continue():
206 """Create the statement that lower_jumps.cpp lowers a continue
209 return assign_x('execute_flag', const_bool(False))
211 def lowered_break_simple():
212 """Create the statement that lower_jumps.cpp lowers a break
213 statement to, in situations where it does not need to clear the
216 return assign_x('break_flag', const_bool(True))
219 """Create the statement that lower_jumps.cpp lowers a break
220 statement to, in situations where it needs to clear the execute
223 return lowered_break_simple() + assign_x('execute_flag', const_bool(False))
225 def if_execute_flag(statements
):
226 """Wrap statements in an if test so that they will only execute if
227 execute_flag is True.
229 check_sexp(statements
)
230 return [['if', ['var_ref', 'execute_flag'], statements
, []]]
232 def if_return_flag(then_statements
, else_statements
):
233 """Wrap statements in an if test with return_flag as the condition.
235 check_sexp(then_statements
)
236 check_sexp(else_statements
)
237 return [['if', ['var_ref', 'return_flag'], then_statements
, else_statements
]]
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.
243 check_sexp(statements
)
244 return [['if', ['var_ref', 'return_flag'], [], statements
]]
247 """Create the return statement that lower_jumps.cpp places at the
248 end of a function when lowering returns.
250 return [['return', ['var_ref', 'return_value']]]
253 """Create the conditional break statement that lower_jumps.cpp
254 places at the end of a function when lowering breaks.
256 return [['if', ['var_ref', 'break_flag'], break_(), []]]
258 def bash_quote(*args
):
259 """Quote the arguments appropriately so that bash will understand
260 each argument as a single word.
262 def quote_word(word
):
264 if not (c
.isalpha() or c
.isdigit() or c
in '@%_-+=:,./'):
270 return "'{0}'".format(word
.replace("'", "'\"'\"'"))
271 return ' '.join(quote_word(word
) for word
in args
)
273 def create_test_case(input_sexp
, expected_sexp
, test_name
,
274 pull_out_jumps
=False, lower_sub_return
=False,
275 lower_main_return
=False, lower_continue
=False,
277 """Create a test case that verifies that do_lower_jumps transforms
278 the given code in the expected way.
280 check_sexp(input_sexp
)
281 check_sexp(expected_sexp
)
282 input_str
= sexp_to_string(sort_decls(input_sexp
))
283 expected_output
= sexp_to_string(sort_decls(expected_sexp
)) # XXX: don't stringify this
285 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format(
286 pull_out_jumps
, lower_sub_return
, lower_main_return
,
287 lower_continue
, lower_break
))
289 return (test_name
, optimization
, input_str
, expected_output
)
291 def test_lower_returns_main():
292 """Test that do_lower_jumps respects the lower_main_return flag in deciding
293 whether to lower returns in the main function.
295 input_sexp
= make_test_case('main', 'void', (
296 complex_if('', return_())
298 expected_sexp
= make_test_case('main', 'void', (
299 declare_execute_flag() +
300 declare_return_flag() +
301 complex_if('', lowered_return())
303 yield create_test_case(
304 input_sexp
, expected_sexp
, 'lower_returns_main_true',
305 lower_main_return
=True)
306 yield create_test_case(
307 input_sexp
, input_sexp
, 'lower_returns_main_false',
308 lower_main_return
=False)
310 def test_lower_returns_sub():
311 """Test that do_lower_jumps respects the lower_sub_return flag in deciding
312 whether to lower returns in subroutines.
314 input_sexp
= make_test_case('sub', 'void', (
315 complex_if('', return_())
317 expected_sexp
= make_test_case('sub', 'void', (
318 declare_execute_flag() +
319 declare_return_flag() +
320 complex_if('', lowered_return())
322 yield create_test_case(
323 input_sexp
, expected_sexp
, 'lower_returns_sub_true',
324 lower_sub_return
=True)
325 yield create_test_case(
326 input_sexp
, input_sexp
, 'lower_returns_sub_false',
327 lower_sub_return
=False)
329 def test_lower_returns_1():
330 """Test that a void return at the end of a function is eliminated."""
331 input_sexp
= make_test_case('main', 'void', (
332 assign_x('a', const_float(1)) +
335 expected_sexp
= make_test_case('main', 'void', (
336 assign_x('a', const_float(1))
338 yield create_test_case(
339 input_sexp
, expected_sexp
, 'lower_returns_1', lower_main_return
=True)
341 def test_lower_returns_2():
342 """Test that lowering is not performed on a non-void return at the end of
345 input_sexp
= make_test_case('sub', 'float', (
346 assign_x('a', const_float(1)) +
347 return_(const_float(1))
349 yield create_test_case(
350 input_sexp
, input_sexp
, 'lower_returns_2', lower_sub_return
=True)
352 def test_lower_returns_3():
353 """Test lowering of returns when there is one nested inside a complex
354 structure of ifs, and one at the end of a function.
356 In this case, the latter return needs to be lowered because it will not be
357 at the end of the function once the final return is inserted.
359 input_sexp
= make_test_case('sub', 'float', (
360 complex_if('', return_(const_float(1))) +
361 return_(const_float(2))
363 expected_sexp
= make_test_case('sub', 'float', (
364 declare_execute_flag() +
365 declare_return_value() +
366 declare_return_flag() +
367 complex_if('', lowered_return(const_float(1))) +
368 if_execute_flag(lowered_return(const_float(2))) +
371 yield create_test_case(
372 input_sexp
, expected_sexp
, 'lower_returns_3', lower_sub_return
=True)
374 def test_lower_returns_4():
375 """Test that returns are properly lowered when they occur in both branches
378 input_sexp
= make_test_case('sub', 'float', (
379 simple_if('a', return_(const_float(1)),
380 return_(const_float(2)))
382 expected_sexp
= make_test_case('sub', 'float', (
383 declare_execute_flag() +
384 declare_return_value() +
385 declare_return_flag() +
386 simple_if('a', lowered_return(const_float(1)),
387 lowered_return(const_float(2))) +
390 yield create_test_case(
391 input_sexp
, expected_sexp
, 'lower_returns_4', lower_sub_return
=True)
393 def test_lower_unified_returns():
394 """If both branches of an if statement end in a return, and pull_out_jumps
395 is True, then those returns should be lifted outside the if and then
398 Verify that this lowering occurs during the same pass as the lowering of
399 other returns by checking that extra temporary variables aren't generated.
401 input_sexp
= make_test_case('main', 'void', (
402 complex_if('a', return_()) +
403 simple_if('b', simple_if('c', return_(), return_()))
405 expected_sexp
= make_test_case('main', 'void', (
406 declare_execute_flag() +
407 declare_return_flag() +
408 complex_if('a', lowered_return()) +
409 if_execute_flag(simple_if('b', (simple_if('c', [], []) +
412 yield create_test_case(
413 input_sexp
, expected_sexp
, 'lower_unified_returns',
414 lower_main_return
=True, pull_out_jumps
=True)
416 def test_lower_pulled_out_jump():
417 doc_string
= """If one branch of an if ends in a jump, and control cannot
418 fall out the bottom of the other branch, and pull_out_jumps is
419 True, then the jump is lifted outside the if.
421 Verify that this lowering occurs during the same pass as the
422 lowering of other jumps by checking that extra temporary
423 variables aren't generated.
425 input_sexp
= make_test_case('main', 'void', (
426 complex_if('a', return_()) +
427 loop(simple_if('b', simple_if('c', break_(), continue_()),
429 assign_x('d', const_float(1))
431 # Note: optimization produces two other effects: the break
432 # gets lifted out of the if statements, and the code after the
433 # loop gets guarded so that it only executes if the return
435 expected_sexp
= make_test_case('main', 'void', (
436 declare_execute_flag() +
437 declare_return_flag() +
438 complex_if('a', lowered_return()) +
440 loop(simple_if('b', simple_if('c', [], continue_()),
441 lowered_return_simple()) +
444 if_return_flag(assign_x('return_flag', const_bool(1)) +
445 assign_x('execute_flag', const_bool(0)),
446 assign_x('d', const_float(1))))
448 yield create_test_case(
449 input_sexp
, expected_sexp
, 'lower_pulled_out_jump',
450 lower_main_return
=True, pull_out_jumps
=True)
452 def test_lower_breaks_1():
453 """If a loop contains an unconditional break at the bottom of it, it should
456 input_sexp
= make_test_case('main', 'void', (
457 loop(assign_x('a', const_float(1)) +
460 expected_sexp
= input_sexp
461 yield create_test_case(
462 input_sexp
, expected_sexp
, 'lower_breaks_1', lower_break
=True)
464 def test_lower_breaks_2():
465 """If a loop contains a conditional break at the bottom of it, it should
466 not be lowered if it is in the then-clause.
468 input_sexp
= make_test_case('main', 'void', (
469 loop(assign_x('a', const_float(1)) +
470 simple_if('b', break_()))
472 expected_sexp
= input_sexp
473 yield create_test_case(
474 input_sexp
, expected_sexp
, 'lower_breaks_2', lower_break
=True)
476 def test_lower_breaks_3():
477 """If a loop contains a conditional break at the bottom of it, it should
478 not be lowered if it is in the then-clause, even if there are statements
481 input_sexp
= make_test_case('main', 'void', (
482 loop(assign_x('a', const_float(1)) +
483 simple_if('b', (assign_x('c', const_float(1)) +
486 expected_sexp
= input_sexp
487 yield create_test_case(
488 input_sexp
, expected_sexp
, 'lower_breaks_3', lower_break
=True)
490 def test_lower_breaks_4():
491 """If a loop contains a conditional break at the bottom of it, it should
492 not be lowered if it is in the else-clause.
494 input_sexp
= make_test_case('main', 'void', (
495 loop(assign_x('a', const_float(1)) +
496 simple_if('b', [], break_()))
498 expected_sexp
= input_sexp
499 yield create_test_case(
500 input_sexp
, expected_sexp
, 'lower_breaks_4', lower_break
=True)
502 def test_lower_breaks_5():
503 """If a loop contains a conditional break at the bottom of it, it should
504 not be lowered if it is in the else-clause, even if there are statements
507 input_sexp
= make_test_case('main', 'void', (
508 loop(assign_x('a', const_float(1)) +
509 simple_if('b', [], (assign_x('c', const_float(1)) +
512 expected_sexp
= input_sexp
513 yield create_test_case(
514 input_sexp
, expected_sexp
, 'lower_breaks_5', lower_break
=True)
516 def test_lower_breaks_6():
517 """If a loop contains conditional breaks and continues, and ends in an
518 unconditional break, then the unconditional break needs to be lowered,
519 because it will no longer be at the end of the loop after the final break
522 input_sexp
= make_test_case('main', 'void', (
523 loop(simple_if('a', (complex_if('b', continue_()) +
524 complex_if('c', break_()))) +
527 expected_sexp
= make_test_case('main', 'void', (
528 declare_break_flag() +
529 loop(declare_execute_flag() +
532 (complex_if('b', lowered_continue()) +
534 complex_if('c', lowered_break())))) +
535 if_execute_flag(lowered_break_simple()) +
538 yield create_test_case(
539 input_sexp
, expected_sexp
, 'lower_breaks_6', lower_break
=True,
542 def test_lower_guarded_conditional_break():
543 """Normally a conditional break at the end of a loop isn't lowered, however
544 if the conditional break gets placed inside an if(execute_flag) because of
545 earlier lowering of continues, then the break needs to be lowered.
547 input_sexp
= make_test_case('main', 'void', (
548 loop(complex_if('a', continue_()) +
549 simple_if('b', break_()))
551 expected_sexp
= make_test_case('main', 'void', (
552 declare_break_flag() +
553 loop(declare_execute_flag() +
554 complex_if('a', lowered_continue()) +
555 if_execute_flag(simple_if('b', lowered_break())) +
558 yield create_test_case(
559 input_sexp
, expected_sexp
, 'lower_guarded_conditional_break',
560 lower_break
=True, lower_continue
=True)
562 def test_remove_continue_at_end_of_loop():
563 """Test that a redundant continue-statement at the end of a loop is
566 input_sexp
= make_test_case('main', 'void', (
567 loop(assign_x('a', const_float(1)) +
570 expected_sexp
= make_test_case('main', 'void', (
571 loop(assign_x('a', const_float(1)))
573 yield create_test_case(input_sexp
, expected_sexp
, 'remove_continue_at_end_of_loop')
575 def test_lower_return_void_at_end_of_loop():
576 """Test that a return of void at the end of a loop is properly lowered."""
577 input_sexp
= make_test_case('main', 'void', (
578 loop(assign_x('a', const_float(1)) +
580 assign_x('b', const_float(2))
582 expected_sexp
= make_test_case('main', 'void', (
583 declare_execute_flag() +
584 declare_return_flag() +
585 loop(assign_x('a', const_float(1)) +
586 lowered_return_simple() +
588 if_return_flag(assign_x('return_flag', const_bool(1)) +
589 assign_x('execute_flag', const_bool(0)),
590 assign_x('b', const_float(2)))
592 yield create_test_case(
593 input_sexp
, input_sexp
, 'return_void_at_end_of_loop_lower_nothing')
594 yield create_test_case(
595 input_sexp
, expected_sexp
, 'return_void_at_end_of_loop_lower_return',
596 lower_main_return
=True)
597 yield create_test_case(
598 input_sexp
, expected_sexp
,
599 'return_void_at_end_of_loop_lower_return_and_break',
600 lower_main_return
=True, lower_break
=True)
602 def test_lower_return_non_void_at_end_of_loop():
603 """Test that a non-void return at the end of a loop is properly lowered."""
604 input_sexp
= make_test_case('sub', 'float', (
605 loop(assign_x('a', const_float(1)) +
606 return_(const_float(2))) +
607 assign_x('b', const_float(3)) +
608 return_(const_float(4))
610 expected_sexp
= make_test_case('sub', 'float', (
611 declare_execute_flag() +
612 declare_return_value() +
613 declare_return_flag() +
614 loop(assign_x('a', const_float(1)) +
615 lowered_return_simple(const_float(2)) +
617 if_return_flag(assign_x('return_value', '(var_ref return_value)') +
618 assign_x('return_flag', const_bool(1)) +
619 assign_x('execute_flag', const_bool(0)),
620 assign_x('b', const_float(3)) +
621 lowered_return(const_float(4))) +
624 yield create_test_case(
625 input_sexp
, input_sexp
, 'return_non_void_at_end_of_loop_lower_nothing')
626 yield create_test_case(
627 input_sexp
, expected_sexp
,
628 'return_non_void_at_end_of_loop_lower_return', lower_sub_return
=True)
629 yield create_test_case(
630 input_sexp
, expected_sexp
,
631 'return_non_void_at_end_of_loop_lower_return_and_break',
632 lower_sub_return
=True, lower_break
=True)
635 test_lower_breaks_1
, test_lower_breaks_2
, test_lower_breaks_3
,
636 test_lower_breaks_4
, test_lower_breaks_5
, test_lower_breaks_6
,
637 test_lower_guarded_conditional_break
, test_lower_pulled_out_jump
,
638 test_lower_return_non_void_at_end_of_loop
,
639 test_lower_return_void_at_end_of_loop
,
640 test_lower_returns_1
, test_lower_returns_2
, test_lower_returns_3
,
641 test_lower_returns_4
, test_lower_returns_main
, test_lower_returns_sub
,
642 test_lower_unified_returns
, test_remove_continue_at_end_of_loop
,