glsl: move to compiler/
[mesa.git] / src / compiler / glsl / tests / lower_jumps / create_test_cases.py
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('#!/usr/bin/env 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()