Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 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 | |||
112 | } else { |
||
113 | |||
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) |
||
135 | """ |
||
136 | return [['declare', ['temporary'], var_type, var_name]] |
||
137 | |||
138 | def assign_x(var_name, value): |
||
139 | """Create a statement that assigns |
||
140 |
|
||
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 ( |
||
149 | if ( |
||
150 | |||
151 | } |
||
152 | } |
||
153 | |||
154 | This is useful in testing jump lowering, because if |
||
155 | ends in a jump, lower_jumps.cpp won't try to combine this |
||
156 | construct with the code that follows it, as it might do for a |
||
157 | simple if. |
||
158 | |||
159 | All variables used in the if statement are prefixed with |
||
160 | var_prefix. This can be used to ensure uniqueness. |
||
161 | """ |
||
162 | check_sexp(statements) |
||
163 | return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements)) |
||
164 | |||
165 | def declare_execute_flag(): |
||
166 | """Create the statements that lower_jumps.cpp uses to declare and |
||
167 | initialize the temporary boolean execute_flag. |
||
168 | """ |
||
169 | return declare_temp('bool', 'execute_flag') + \ |
||
170 | assign_x('execute_flag', const_bool(True)) |
||
171 | |||
172 | def declare_return_flag(): |
||
173 | """Create the statements that lower_jumps.cpp uses to declare and |
||
174 | initialize the temporary boolean return_flag. |
||
175 | """ |
||
176 | return declare_temp('bool', 'return_flag') + \ |
||
177 | assign_x('return_flag', const_bool(False)) |
||
178 | |||
179 | def declare_return_value(): |
||
180 | """Create the statements that lower_jumps.cpp uses to declare and |
||
181 | initialize the temporary variable return_value. Assume that |
||
182 | return_value is a float. |
||
183 | """ |
||
184 | return declare_temp('float', 'return_value') |
||
185 | |||
186 | def declare_break_flag(): |
||
187 | """Create the statements that lower_jumps.cpp uses to declare and |
||
188 | initialize the temporary boolean break_flag. |
||
189 | """ |
||
190 | return declare_temp('bool', 'break_flag') + \ |
||
191 | assign_x('break_flag', const_bool(False)) |
||
192 | |||
193 | def lowered_return_simple(value = None): |
||
194 | """Create the statements that lower_jumps.cpp lowers a return |
||
195 | statement to, in situations where it does not need to clear the |
||
196 | execute flag. |
||
197 | """ |
||
198 | if value: |
||
199 | result = assign_x('return_value', value) |
||
200 | else: |
||
201 | result = [] |
||
202 | return result + assign_x('return_flag', const_bool(True)) |
||
203 | |||
204 | def lowered_return(value = None): |
||
205 | """Create the statements that lower_jumps.cpp lowers a return |
||
206 | statement to, in situations where it needs to clear the execute |
||
207 | flag. |
||
208 | """ |
||
209 | return lowered_return_simple(value) + \ |
||
210 | assign_x('execute_flag', const_bool(False)) |
||
211 | |||
212 | def lowered_continue(): |
||
213 | """Create the statement that lower_jumps.cpp lowers a continue |
||
214 | statement to. |
||
215 | """ |
||
216 | return assign_x('execute_flag', const_bool(False)) |
||
217 | |||
218 | def lowered_break_simple(): |
||
219 | """Create the statement that lower_jumps.cpp lowers a break |
||
220 | statement to, in situations where it does not need to clear the |
||
221 | execute flag. |
||
222 | """ |
||
223 | return assign_x('break_flag', const_bool(True)) |
||
224 | |||
225 | def lowered_break(): |
||
226 | """Create the statement that lower_jumps.cpp lowers a break |
||
227 | statement to, in situations where it needs to clear the execute |
||
228 | flag. |
||
229 | """ |
||
230 | return lowered_break_simple() + assign_x('execute_flag', const_bool(False)) |
||
231 | |||
232 | def if_execute_flag(statements): |
||
233 | """Wrap statements in an if test so that they will only execute if |
||
234 | execute_flag is True. |
||
235 | """ |
||
236 | check_sexp(statements) |
||
237 | return [['if', ['var_ref', 'execute_flag'], statements, []]] |
||
238 | |||
239 | def if_not_return_flag(statements): |
||
240 | """Wrap statements in an if test so that they will only execute if |
||
241 | return_flag is False. |
||
242 | """ |
||
243 | check_sexp(statements) |
||
244 | return [['if', ['var_ref', 'return_flag'], [], statements]] |
||
245 | |||
246 | def final_return(): |
||
247 | """Create the return statement that lower_jumps.cpp places at the |
||
248 | end of a function when lowering returns. |
||
249 | """ |
||
250 | return [['return', ['var_ref', 'return_value']]] |
||
251 | |||
252 | def final_break(): |
||
253 | """Create the conditional break statement that lower_jumps.cpp |
||
254 | places at the end of a function when lowering breaks. |
||
255 | """ |
||
256 | return [['if', ['var_ref', 'break_flag'], break_(), []]] |
||
257 | |||
258 | def bash_quote(*args): |
||
259 | """Quote the arguments appropriately so that bash will understand |
||
260 | each argument as a single word. |
||
261 | """ |
||
262 | def quote_word(word): |
||
263 | for c in word: |
||
264 | if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'): |
||
265 | break |
||
266 | else: |
||
267 | if not word: |
||
268 | return "''" |
||
269 | return word |
||
270 | return "'{0}'".format(word.replace("'", "'\"'\"'")) |
||
271 | return ' '.join(quote_word(word) for word in args) |
||
272 | |||
273 | def create_test_case(doc_string, input_sexp, expected_sexp, test_name, |
||
274 | pull_out_jumps=False, lower_sub_return=False, |
||
275 | lower_main_return=False, lower_continue=False, |
||
276 | lower_break=False): |
||
277 | """Create a test case that verifies that do_lower_jumps transforms |
||
278 | the given code in the expected way. |
||
279 | """ |
||
280 | doc_lines = [line.strip() for line in doc_string.splitlines()] |
||
281 | doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '') |
||
282 | check_sexp(input_sexp) |
||
283 | check_sexp(expected_sexp) |
||
284 | input_str = sexp_to_string(sort_decls(input_sexp)) |
||
285 | expected_output = sexp_to_string(sort_decls(expected_sexp)) |
||
286 | |||
287 | optimization = ( |
||
288 | 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format( |
||
289 | pull_out_jumps, lower_sub_return, lower_main_return, |
||
290 | lower_continue, lower_break)) |
||
291 | args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization] |
||
292 | test_file = '{0}.opt_test'.format(test_name) |
||
293 | with open(test_file, 'w') as f: |
||
294 | f.write('#!/bin/bash\n#\n# This file was generated by create_test_cases.py.\n#\n') |
||
295 | f.write(doc_string) |
||
296 | f.write('{0} < |
||
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() |