# coding=utf-8 # # Copyright © 2011 Intel Corporation # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice (including the next # paragraph) shall be included in all copies or substantial portions of the # Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. import os import os.path import re import subprocess import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # For access to sexps.py, which is in parent dir from sexps import * def make_test_case(f_name, ret_type, body): """Create a simple optimization test case consisting of a single function with the given name, return type, and body. Global declarations are automatically created for any undeclared variables that are referenced by the function. All undeclared variables are assumed to be floats. """ check_sexp(body) declarations = {} def make_declarations(sexp, already_declared = ()): if isinstance(sexp, list): if len(sexp) == 2 and sexp[0] == 'var_ref': if sexp[1] not in already_declared: declarations[sexp[1]] = [ 'declare', ['in'], 'float', sexp[1]] elif len(sexp) == 4 and sexp[0] == 'assign': assert sexp[2][0] == 'var_ref' if sexp[2][1] not in already_declared: declarations[sexp[2][1]] = [ 'declare', ['out'], 'float', sexp[2][1]] make_declarations(sexp[3], already_declared) else: already_declared = set(already_declared) for s in sexp: if isinstance(s, list) and len(s) >= 4 and \ s[0] == 'declare': already_declared.add(s[3]) else: make_declarations(s, already_declared) make_declarations(body) return declarations.values() + \ [['function', f_name, ['signature', ret_type, ['parameters'], body]]] # The following functions can be used to build expressions. def const_float(value): """Create an expression representing the given floating point value.""" return ['constant', 'float', ['{0:.6f}'.format(value)]] def const_bool(value): """Create an expression representing the given boolean value. If value is not a boolean, it is converted to a boolean. So, for instance, const_bool(1) is equivalent to const_bool(True). """ return ['constant', 'bool', ['{0}'.format(1 if value else 0)]] def gt_zero(var_name): """Create Construct the expression var_name > 0""" return ['expression', 'bool', '>', ['var_ref', var_name], const_float(0)] # The following functions can be used to build complex control flow # statements. All of these functions return statement lists (even # those which only create a single statement), so that statements can # be sequenced together using the '+' operator. def return_(value = None): """Create a return statement.""" if value is not None: return [['return', value]] else: return [['return']] def break_(): """Create a break statement.""" return ['break'] def continue_(): """Create a continue statement.""" return ['continue'] def simple_if(var_name, then_statements, else_statements = None): """Create a statement of the form if (var_name > 0.0) { } else { } else_statements may be omitted. """ if else_statements is None: else_statements = [] check_sexp(then_statements) check_sexp(else_statements) return [['if', gt_zero(var_name), then_statements, else_statements]] def loop(statements): """Create a loop containing the given statements as its loop body. """ check_sexp(statements) return [['loop', statements]] def declare_temp(var_type, var_name): """Create a declaration of the form (declare (temporary) to the variable . The assignment uses the mask (x). """ check_sexp(value) return [['assign', ['x'], ['var_ref', var_name], value]] def complex_if(var_prefix, statements): """Create a statement of the form if (a > 0.0) { if (b > 0.0) { } } This is useful in testing jump lowering, because if ends in a jump, lower_jumps.cpp won't try to combine this construct with the code that follows it, as it might do for a simple if. All variables used in the if statement are prefixed with var_prefix. This can be used to ensure uniqueness. """ check_sexp(statements) return simple_if(var_prefix + 'a', simple_if(var_prefix + 'b', statements)) def declare_execute_flag(): """Create the statements that lower_jumps.cpp uses to declare and initialize the temporary boolean execute_flag. """ return declare_temp('bool', 'execute_flag') + \ assign_x('execute_flag', const_bool(True)) def declare_return_flag(): """Create the statements that lower_jumps.cpp uses to declare and initialize the temporary boolean return_flag. """ return declare_temp('bool', 'return_flag') + \ assign_x('return_flag', const_bool(False)) def declare_return_value(): """Create the statements that lower_jumps.cpp uses to declare and initialize the temporary variable return_value. Assume that return_value is a float. """ return declare_temp('float', 'return_value') def declare_break_flag(): """Create the statements that lower_jumps.cpp uses to declare and initialize the temporary boolean break_flag. """ return declare_temp('bool', 'break_flag') + \ assign_x('break_flag', const_bool(False)) def lowered_return_simple(value = None): """Create the statements that lower_jumps.cpp lowers a return statement to, in situations where it does not need to clear the execute flag. """ if value: result = assign_x('return_value', value) else: result = [] return result + assign_x('return_flag', const_bool(True)) def lowered_return(value = None): """Create the statements that lower_jumps.cpp lowers a return statement to, in situations where it needs to clear the execute flag. """ return lowered_return_simple(value) + \ assign_x('execute_flag', const_bool(False)) def lowered_continue(): """Create the statement that lower_jumps.cpp lowers a continue statement to. """ return assign_x('execute_flag', const_bool(False)) def lowered_break_simple(): """Create the statement that lower_jumps.cpp lowers a break statement to, in situations where it does not need to clear the execute flag. """ return assign_x('break_flag', const_bool(True)) def lowered_break(): """Create the statement that lower_jumps.cpp lowers a break statement to, in situations where it needs to clear the execute flag. """ return lowered_break_simple() + assign_x('execute_flag', const_bool(False)) def if_execute_flag(statements): """Wrap statements in an if test so that they will only execute if execute_flag is True. """ check_sexp(statements) return [['if', ['var_ref', 'execute_flag'], statements, []]] def if_return_flag(then_statements, else_statements): """Wrap statements in an if test with return_flag as the condition. """ check_sexp(then_statements) check_sexp(else_statements) return [['if', ['var_ref', 'return_flag'], then_statements, else_statements]] def if_not_return_flag(statements): """Wrap statements in an if test so that they will only execute if return_flag is False. """ check_sexp(statements) return [['if', ['var_ref', 'return_flag'], [], statements]] def final_return(): """Create the return statement that lower_jumps.cpp places at the end of a function when lowering returns. """ return [['return', ['var_ref', 'return_value']]] def final_break(): """Create the conditional break statement that lower_jumps.cpp places at the end of a function when lowering breaks. """ return [['if', ['var_ref', 'break_flag'], break_(), []]] def bash_quote(*args): """Quote the arguments appropriately so that bash will understand each argument as a single word. """ def quote_word(word): for c in word: if not (c.isalpha() or c.isdigit() or c in '@%_-+=:,./'): break else: if not word: return "''" return word return "'{0}'".format(word.replace("'", "'\"'\"'")) return ' '.join(quote_word(word) for word in args) def create_test_case(doc_string, input_sexp, expected_sexp, test_name, pull_out_jumps=False, lower_sub_return=False, lower_main_return=False, lower_continue=False, lower_break=False): """Create a test case that verifies that do_lower_jumps transforms the given code in the expected way. """ doc_lines = [line.strip() for line in doc_string.splitlines()] doc_string = ''.join('# {0}\n'.format(line) for line in doc_lines if line != '') check_sexp(input_sexp) check_sexp(expected_sexp) input_str = sexp_to_string(sort_decls(input_sexp)) expected_output = sexp_to_string(sort_decls(expected_sexp)) optimization = ( 'do_lower_jumps({0:d}, {1:d}, {2:d}, {3:d}, {4:d})'.format( pull_out_jumps, lower_sub_return, lower_main_return, lower_continue, lower_break)) args = ['../../glsl_test', 'optpass', '--quiet', '--input-ir', optimization] test_file = '{0}.opt_test'.format(test_name) with open(test_file, 'w') as f: f.write('#!/usr/bin/env bash\n#\n# This file was generated by create_test_cases.py.\n#\n') f.write(doc_string) f.write('{0} <