Chromium Code Reviews| Index: pydir/crosstest_generator.py |
| diff --git a/pydir/crosstest_generator.py b/pydir/crosstest_generator.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..7264818f1d75865691a67bdab49fcaba3e0faeb6 |
| --- /dev/null |
| +++ b/pydir/crosstest_generator.py |
| @@ -0,0 +1,210 @@ |
| +#!/usr/bin/env python2 |
| + |
| +import argparse |
| +import os |
| +import shutil |
| +import sys |
| + |
| +from utils import shellcmd |
| +from utils import FindBaseNaCl |
| + |
| +def Match(desc, includes, excludes, default_match): |
| + """Determines whether desc is a match against includes and excludes. |
| + |
| + 'desc' is a set of attributes, and 'includes' and 'excludes' are lists of sets |
| + of attributes. |
| + |
| + If 'desc' matches any element from 'excludes', the result is False. |
| + Otherwise, if 'desc' matches any element from 'includes', the result is True. |
| + Otherwise, the 'default_match' value is returned. |
| + """ |
| + for exclude in excludes: |
| + if exclude < desc: |
| + return False |
| + for include in includes: |
| + if include < desc: |
| + return True |
| + return default_match |
| + |
| +def main(): |
| + """Framework for cross test generation and execution. |
| + |
| + Builds and executes cross tests from the space of all possible attribute |
| + combinations. The space can be restricted by providing subsets of attributes |
| + to specifically include or exclude. |
| + """ |
| + # Defines the set of tests and each test's inputs. Each test maps to a |
| + # dictionary containing a single driver file, one or more test source files, |
| + # and an optional list of crosstest.py flags for special cases. |
| + tests = { |
|
Mircea Trofin
2015/03/08 16:23:30
will this list potentially grow? If so, is the exp
Jim Stichnoth
2015/03/09 18:16:16
Good point that I should try not to hard-code the
|
| + 'simple_loop' : { |
| + 'driver' : 'simple_loop_main.c', |
| + 'test' : [ 'simple_loop.c' ] |
| + }, |
| + 'mem_intrin' : { |
| + 'driver' : 'mem_intrin_main.cpp', |
| + 'test' : [ 'mem_intrin.cpp' ] |
| + }, |
| + 'test_arith' : { |
| + 'driver' : 'test_arith_main.cpp', |
| + 'test' : [ 'test_arith.cpp', 'test_arith_frem.ll', 'test_arith_sqrt.ll' ] |
| + }, |
| + 'test_bitmanip' : { |
| + 'driver' : 'test_bitmanip_main.cpp', |
| + 'test' : [ 'test_bitmanip.cpp', 'test_bitmanip_intrin.ll' ] |
| + }, |
| + 'test_calling_conv' : { |
| + 'driver' : 'test_calling_conv_main.cpp', |
| + 'test' : [ 'test_calling_conv.cpp' ] |
| + }, |
| + 'test_cast' : { |
| + 'driver' : 'test_cast_main.cpp', |
| + 'test' : [ 'test_cast.cpp', 'test_cast_to_u1.ll', 'test_cast_vectors.ll' ] |
| + }, |
| + 'test_fcmp' : { |
| + 'driver' : 'test_fcmp_main.cpp', |
| + 'test' : [ 'test_fcmp.pnacl.ll' ] |
| + }, |
| + 'test_global' : { |
| + 'driver' : 'test_global_main.cpp', |
| + 'test' : [ 'test_global.cpp' ] |
| + }, |
| + 'test_icmp' : { |
| + 'driver' : 'test_icmp_main.cpp', |
| + 'test' : [ 'test_icmp.cpp', 'test_icmp_i1vec.ll' ] |
| + }, |
| + 'test_select' : { |
| + 'driver' : 'test_select_main.cpp', |
| + 'test' : [ 'test_select.ll' ] |
| + }, |
| + 'test_stacksave' : { |
| + 'driver' : 'test_stacksave_main.c', |
| + 'test' : [ 'test_stacksave.c' ] |
| + }, |
| + 'test_sync_atomic' : { |
| + 'driver' : 'test_sync_atomic_main.cpp', |
| + # Compile the non-subzero object files straight from source since the |
| + # native LLVM backend does not understand how to lower NaCl-specific |
| + # intrinsics. |
| + 'flags' : [ '--crosstest-bitcode=0' ], |
| + 'test' : [ 'test_sync_atomic.cpp' ] |
| + }, |
| + 'test_vector_ops' : { |
| + 'driver' : 'test_vector_ops_main.cpp', |
| + 'test' : [ 'test_vector_ops.ll' ] |
| + }, |
| + } |
| + # The rest of the attribute sets. |
| + targets = [ 'x8632' ] |
| + sandboxing = [ 'native', 'sandbox' ] |
| + opt_levels = [ 'Om1', 'O2' ] |
| + arch_attrs = [ 'sse2', 'sse4.1' ] |
| + # all_keys is only used in the help text. |
| + all_keys = '; '.join([' '.join(sorted(tests.keys())), ' '.join(targets), |
| + ' '.join(sandboxing), ' '.join(opt_levels), |
| + ' '.join(arch_attrs)]) |
| + |
| + root = FindBaseNaCl() |
| + argparser = argparse.ArgumentParser( |
| + description=' ' + main.__doc__ + |
| + 'The set of attributes is the following:\n\n' + all_keys, |
| + formatter_class=argparse.RawTextHelpFormatter) |
| + argparser.add_argument('--include', '-i', default=[], dest='include', |
| + action='append', metavar='ATTR_LIST', |
| + help='Attributes to include (comma-separated). ' + |
| + 'Can be used multiple times.') |
| + argparser.add_argument('--exclude', '-e', default=[], dest='exclude', |
| + action='append', metavar='ATTR_LIST', |
| + help='Attributes to include (comma-separated). ' + |
| + 'Can be used multiple times.') |
| + argparser.add_argument('--verbose', '-v', default=False, action='store_true', |
| + help='Use verbose output') |
| + argparser.add_argument('--defer', default=False, action='store_true', |
| + help='Defer execution until all executables are built') |
| + argparser.add_argument('--no-compile', '-n', default=False, |
| + action='store_true', |
| + help="Don't build; reuse binaries from the last run") |
| + argparser.add_argument('--dir', dest='dir', metavar='DIRECTORY', |
| + default=('{root}/toolchain_build/src/subzero/' + |
| + 'crosstest/Output').format(root=root), |
| + help='Output directory') |
| + argparser.add_argument('--lit', default=False, action='store_true', |
| + help='Generate files for lit testing') |
| + args = argparser.parse_args() |
| + # includes and excludes are both lists of sets. |
| + includes = [ set(item.split(',')) for item in args.include ] |
| + excludes = [ set(item.split(',')) for item in args.exclude ] |
| + # If any --include args are provided, the default is to not match. |
| + default_match = not args.include |
| + |
| + # Delete and recreate the output directory, unless --no-compile was specified. |
| + if not args.no_compile: |
| + if os.path.exists(args.dir): |
| + if os.path.isdir(args.dir): |
| + shutil.rmtree(args.dir) |
| + else: |
| + os.remove(args.dir) |
| + if not os.path.exists(args.dir): |
| + os.makedirs(args.dir) |
| + |
| + # pypath is where to find other Subzero python scripts. |
| + pypath = os.path.abspath(os.path.dirname(sys.argv[0])) |
| + # Run from the crosstest directory to make it easy to grab inputs. |
| + crosstest_dir = '{root}/toolchain_build/src/subzero/crosstest'.format( |
| + root=root) |
| + os.chdir(crosstest_dir) |
| + # If --defer is specified, collect the run commands into deferred_cmds for |
| + # later execution. |
| + deferred_cmds = [] |
| + for test in sorted(tests.keys()): |
| + for target in targets: |
| + for sb in sandboxing: |
| + for opt in opt_levels: |
| + for attr in arch_attrs: |
| + desc = [ test, target, sb, opt, attr ] |
| + if Match(set(desc), includes, excludes, default_match): |
| + exe = '{test}_{target}_{sb}_{opt}_{attr}'.format( |
| + test=test, target=target, sb=sb, opt=opt, |
| + attr=attr) |
| + extra = tests[test]['flags'] if 'flags' in tests[test] else [] |
|
jvoung (off chromium)
2015/03/09 17:34:21
Could probably have done "tests[test].get('flags',
Jim Stichnoth
2015/03/09 18:16:16
Hmm, nice. Unfortunately, I don't think there's a
jvoung (off chromium)
2015/03/09 20:34:59
Hmm I don't see anything like that for ConfigParse
|
| + # Generate the compile command. |
| + cmp_cmd = ['{path}/crosstest.py'.format(path=pypath), |
| + '-{opt}'.format(opt=opt), |
| + '--mattr={attr}'.format(attr=attr), |
| + '--prefix=Subzero_', |
| + '--target={target}'.format(target=target), |
| + '--sandbox={sb}'.format(sb='1' if sb=='sandbox' |
| + else '0'), |
| + '--dir={dir}'.format(dir=args.dir), |
| + '--output={exe}'.format(exe=exe), |
| + '--driver={drv}'.format(drv=tests[test]['driver']), |
| + ] + extra + \ |
| + [ '--test=' + t for t in tests[test]['test'] ] |
| + run_cmd_base = os.path.join(args.dir, exe) |
| + # Generate the run command. |
| + run_cmd = run_cmd_base |
| + if sb == 'sandbox': |
| + run_cmd = '{root}/run.py -q '.format(root=root) + run_cmd |
| + if args.lit: |
| + # Create a file to drive the lit test. |
| + with open(run_cmd_base + '.xtest', 'w') as f: |
| + f.write('# RUN: sh %s | FileCheck %s\n') |
| + f.write('cd ' + crosstest_dir + ' && \\\n') |
| + f.write(' '.join(cmp_cmd) + ' && \\\n') |
| + f.write(run_cmd + '\n') |
| + f.write('echo Recreate a failure using ' + __file__ + |
| + ' --include=' + ','.join(desc) + '\n') |
| + f.write('# CHECK: Failures=0\n') |
| + else: |
| + if not args.no_compile: |
| + shellcmd(cmp_cmd, |
| + echo=args.verbose) |
| + if (args.defer): |
| + deferred_cmds.append(run_cmd) |
| + else: |
| + shellcmd(run_cmd, echo=True) |
| + for run_cmd in deferred_cmds: |
| + shellcmd(run_cmd, echo=True) |
| + |
| +if __name__ == '__main__': |
| + main() |