|  | #!/usr/bin/env python2 | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  | import tempfile | 
|  |  | 
|  | import targets | 
|  | from szbuild import LinkNonsfi | 
|  | from utils import FindBaseNaCl, GetObjcopyCmd, get_sfi_string, shellcmd | 
|  |  | 
|  | def main(): | 
|  | """Builds a cross-test binary for comparing Subzero and llc translation. | 
|  |  | 
|  | Each --test argument is compiled once by llc and once by Subzero.  C/C++ | 
|  | tests are first compiled down to PNaCl bitcode using pnacl-clang and | 
|  | pnacl-opt.  The --prefix argument ensures that symbol names are different | 
|  | between the two object files, to avoid linking errors. | 
|  |  | 
|  | There is also a --driver argument that specifies the C/C++ file that calls | 
|  | the test functions with a variety of interesting inputs and compares their | 
|  | results. | 
|  |  | 
|  | """ | 
|  | # arch_map maps a Subzero target string to TargetInfo (e.g., triple). | 
|  | arch_map = { 'x8632': targets.X8632Target, | 
|  | 'x8664': targets.X8664Target, | 
|  | 'arm32': targets.ARM32Target, | 
|  | 'mips32': targets.MIPS32Target} | 
|  | arch_sz_flags = { 'x8632': [], | 
|  | 'x8664': [], | 
|  | # For ARM, test a large stack offset as well. +/- 4095 is | 
|  | # the limit, so test somewhere near that boundary. | 
|  | 'arm32': ['--test-stack-extra', '4084'], | 
|  | 'mips32': ['--test-stack-extra', '4084'] | 
|  | } | 
|  | arch_llc_flags_extra = { | 
|  | # Use sse2 instructions regardless of input -mattr | 
|  | # argument to avoid differences in (undefined) behavior of | 
|  | # converting NaN to int. | 
|  | 'x8632': ['-mattr=sse2'], | 
|  | 'x8664': ['-mattr=sse2'], | 
|  | 'arm32': [], | 
|  | 'mips32':[], | 
|  | } | 
|  | desc = 'Build a cross-test that compares Subzero and llc translation.' | 
|  | argparser = argparse.ArgumentParser(description=desc) | 
|  | argparser.add_argument('--test', required=True, action='append', | 
|  | metavar='TESTFILE_LIST', | 
|  | help='List of C/C++/.ll files with test functions') | 
|  | argparser.add_argument('--driver', required=True, | 
|  | metavar='DRIVER', | 
|  | help='Driver program') | 
|  | argparser.add_argument('--target', required=False, default='x8632', | 
|  | choices=arch_map.keys(), | 
|  | metavar='TARGET', | 
|  | help='Translation target architecture.' + | 
|  | ' Default %(default)s.') | 
|  | argparser.add_argument('-O', required=False, default='2', dest='optlevel', | 
|  | choices=['m1', '-1', '0', '1', '2'], | 
|  | metavar='OPTLEVEL', | 
|  | help='Optimization level for llc and Subzero ' + | 
|  | '(m1 and -1 are equivalent).' + | 
|  | ' Default %(default)s.') | 
|  | argparser.add_argument('--clang-opt', required=False, default=True, | 
|  | dest='clang_opt') | 
|  | argparser.add_argument('--mattr',  required=False, default='sse2', | 
|  | dest='attr', choices=['sse2', 'sse4.1', | 
|  | 'neon', 'hwdiv-arm', | 
|  | 'base'], | 
|  | metavar='ATTRIBUTE', | 
|  | help='Target attribute. Default %(default)s.') | 
|  | argparser.add_argument('--sandbox', required=False, default=0, type=int, | 
|  | dest='sandbox', | 
|  | help='Use sandboxing. Default "%(default)s".') | 
|  | argparser.add_argument('--nonsfi', required=False, default=0, type=int, | 
|  | dest='nonsfi', | 
|  | help='Use Non-SFI mode. Default "%(default)s".') | 
|  | argparser.add_argument('--prefix', required=True, | 
|  | metavar='SZ_PREFIX', | 
|  | help='String prepended to Subzero symbol names') | 
|  | argparser.add_argument('--output', '-o', required=True, | 
|  | metavar='EXECUTABLE', | 
|  | help='Executable to produce') | 
|  | argparser.add_argument('--dir', required=False, default='.', | 
|  | metavar='OUTPUT_DIR', | 
|  | help='Output directory for all files.' + | 
|  | ' Default "%(default)s".') | 
|  | argparser.add_argument('--filetype', default='obj', dest='filetype', | 
|  | choices=['obj', 'asm', 'iasm'], | 
|  | help='Output file type.  Default %(default)s.') | 
|  | argparser.add_argument('--sz', dest='sz_args', action='append', default=[], | 
|  | help='Extra arguments to pass to pnacl-sz.') | 
|  | args = argparser.parse_args() | 
|  |  | 
|  | nacl_root = FindBaseNaCl() | 
|  | bindir = ('{root}/toolchain/linux_x86/pnacl_newlib_raw/bin' | 
|  | .format(root=nacl_root)) | 
|  | target_info = arch_map[args.target] | 
|  | triple = target_info.triple | 
|  | if args.sandbox: | 
|  | triple = targets.ConvertTripleToNaCl(triple) | 
|  | llc_flags = target_info.llc_flags + arch_llc_flags_extra[args.target] | 
|  | if args.nonsfi: | 
|  | llc_flags.extend(['-relocation-model=pic', | 
|  | '-malign-double', | 
|  | '-force-tls-non-pic', | 
|  | '-mtls-use-call']) | 
|  | mypath = os.path.abspath(os.path.dirname(sys.argv[0])) | 
|  |  | 
|  | # Construct a "unique key" for each test so that tests can be run in | 
|  | # parallel without race conditions on temporary file creation. | 
|  | key = '{sb}.O{opt}.{attr}.{target}'.format( | 
|  | target=args.target, | 
|  | sb=get_sfi_string(args, 'sb', 'nonsfi', 'nat'), | 
|  | opt=args.optlevel, attr=args.attr) | 
|  | objs = [] | 
|  | for arg in args.test: | 
|  | base, ext = os.path.splitext(arg) | 
|  | if ext == '.ll': | 
|  | bitcode = arg | 
|  | else: | 
|  | # Use pnacl-clang and pnacl-opt to produce PNaCl bitcode. | 
|  | bitcode_nonfinal = os.path.join(args.dir, base + '.' + key + '.bc') | 
|  | bitcode = os.path.join(args.dir, base + '.' + key + '.pnacl.ll') | 
|  | shellcmd(['{bin}/pnacl-clang'.format(bin=bindir), | 
|  | ('-O2' if args.clang_opt else '-O0'), | 
|  | ('-DARM32' if args.target == 'arm32' else ''), '-c', arg, | 
|  | ('-DMIPS32' if args.target == 'mips32' else ''), | 
|  | '-o', bitcode_nonfinal]) | 
|  | shellcmd(['{bin}/pnacl-opt'.format(bin=bindir), | 
|  | '-pnacl-abi-simplify-preopt', | 
|  | '-pnacl-abi-simplify-postopt', | 
|  | '-pnaclabi-allow-debug-metadata', | 
|  | '-strip-metadata', | 
|  | '-strip-module-flags', | 
|  | '-strip-debug', | 
|  | bitcode_nonfinal, '-S', '-o', bitcode]) | 
|  |  | 
|  | base_sz = '{base}.{key}'.format(base=base, key=key) | 
|  | asm_sz = os.path.join(args.dir, base_sz + '.sz.s') | 
|  | obj_sz = os.path.join(args.dir, base_sz + '.sz.o') | 
|  | obj_llc = os.path.join(args.dir, base_sz + '.llc.o') | 
|  |  | 
|  | shellcmd(['{path}/pnacl-sz'.format(path=os.path.dirname(mypath)), | 
|  | ] + args.sz_args + [ | 
|  | '-O' + args.optlevel, | 
|  | '-mattr=' + args.attr, | 
|  | '--target=' + args.target, | 
|  | '--sandbox=' + str(args.sandbox), | 
|  | '--nonsfi=' + str(args.nonsfi), | 
|  | '--prefix=' + args.prefix, | 
|  | '-allow-uninitialized-globals', | 
|  | '-externalize', | 
|  | '-filetype=' + args.filetype, | 
|  | '-o=' + (obj_sz if args.filetype == 'obj' else asm_sz), | 
|  | bitcode] + arch_sz_flags[args.target]) | 
|  | if args.filetype != 'obj': | 
|  | shellcmd(['{bin}/llvm-mc'.format(bin=bindir), | 
|  | '-triple=' + ('mipsel-linux-gnu' | 
|  | if args.target == 'mips32' and args.sandbox | 
|  | else triple), | 
|  | '-filetype=obj', | 
|  | '-o=' + obj_sz, | 
|  | asm_sz]) | 
|  |  | 
|  | # Each separately translated Subzero object file contains its own | 
|  | # definition of the __Sz_block_profile_info profiling symbol.  Avoid | 
|  | # linker errors (multiply defined symbol) by making all copies weak. | 
|  | # (This could also be done by Subzero if it supported weak symbol | 
|  | # definitions.)  This approach should be OK because cross tests are | 
|  | # currently the only situation where multiple translated files are | 
|  | # linked into the executable, but when PNaCl supports shared nexe | 
|  | # libraries, this would need to change.  (Note: the same issue applies | 
|  | # to the __Sz_revision symbol.) | 
|  | shellcmd(['{bin}/{objcopy}'.format(bin=bindir, | 
|  | objcopy=GetObjcopyCmd(args.target)), | 
|  | '--weaken-symbol=__Sz_block_profile_info', | 
|  | '--weaken-symbol=__Sz_revision', | 
|  | '--strip-symbol=nacl_tp_tdb_offset', | 
|  | '--strip-symbol=nacl_tp_tls_offset', | 
|  | obj_sz]) | 
|  | objs.append(obj_sz) | 
|  | shellcmd(['{bin}/pnacl-llc'.format(bin=bindir), | 
|  | '-mtriple=' + triple, | 
|  | '-externalize', | 
|  | '-filetype=obj', | 
|  | '-bitcode-format=llvm', | 
|  | '-o=' + obj_llc, | 
|  | bitcode] + llc_flags) | 
|  | strip_syms = [] if args.target == 'mips32' else ['nacl_tp_tdb_offset', | 
|  | 'nacl_tp_tls_offset'] | 
|  | shellcmd(['{bin}/{objcopy}'.format(bin=bindir, | 
|  | objcopy=GetObjcopyCmd(args.target)), | 
|  | obj_llc] + | 
|  | [('--strip-symbol=' + sym) for sym in strip_syms]) | 
|  | objs.append(obj_llc) | 
|  |  | 
|  | # Add szrt_sb_${target}.o or szrt_native_${target}.o. | 
|  | if not args.nonsfi: | 
|  | objs.append(( | 
|  | '{root}/toolchain_build/src/subzero/build/runtime/' + | 
|  | 'szrt_{sb}_' + args.target + '.o' | 
|  | ).format(root=nacl_root, | 
|  | sb=get_sfi_string(args, 'sb', 'nonsfi', 'native'))) | 
|  |  | 
|  | target_params = [] | 
|  |  | 
|  | if args.target == 'arm32': | 
|  | target_params.append('-DARM32') | 
|  | target_params.append('-static') | 
|  |  | 
|  | if args.target == 'mips32': | 
|  | target_params.append('-DMIPS32') | 
|  |  | 
|  | pure_c = os.path.splitext(args.driver)[1] == '.c' | 
|  | if not args.nonsfi: | 
|  | # Set compiler to clang, clang++, pnacl-clang, or pnacl-clang++. | 
|  | compiler = '{bin}/{prefix}{cc}'.format( | 
|  | bin=bindir, prefix=get_sfi_string(args, 'pnacl-', '', ''), | 
|  | cc='clang' if pure_c else 'clang++') | 
|  | sb_native_args = (['-O0', '--pnacl-allow-native', | 
|  | '-arch', target_info.compiler_arch, | 
|  | '-Wn,-defsym=__Sz_AbsoluteZero=0'] | 
|  | if args.sandbox else | 
|  | ['-g', '-target=' + triple, | 
|  | '-lm', '-lpthread', | 
|  | '-Wl,--defsym=__Sz_AbsoluteZero=0'] + | 
|  | target_info.cross_headers) | 
|  | shellcmd([compiler] + target_params + [args.driver] + objs + | 
|  | ['-o', os.path.join(args.dir, args.output)] + sb_native_args) | 
|  | return 0 | 
|  |  | 
|  | base, ext = os.path.splitext(args.driver) | 
|  | bitcode_nonfinal = os.path.join(args.dir, base + '.' + key + '.bc') | 
|  | bitcode = os.path.join(args.dir, base + '.' + key + '.pnacl.ll') | 
|  | asm_sz = os.path.join(args.dir, base + '.' + key + '.s') | 
|  | obj_llc = os.path.join(args.dir, base + '.' + key + '.o') | 
|  | compiler = '{bin}/{prefix}{cc}'.format( | 
|  | bin=bindir, prefix='pnacl-', | 
|  | cc='clang' if pure_c else 'clang++') | 
|  | shellcmd([compiler] + target_params + [ | 
|  | args.driver, | 
|  | '-O2', | 
|  | '-o', bitcode_nonfinal, | 
|  | '-Wl,-r' | 
|  | ]) | 
|  | shellcmd(['{bin}/pnacl-opt'.format(bin=bindir), | 
|  | '-pnacl-abi-simplify-preopt', | 
|  | '-pnacl-abi-simplify-postopt', | 
|  | '-pnaclabi-allow-debug-metadata', | 
|  | '-strip-metadata', | 
|  | '-strip-module-flags', | 
|  | '-strip-debug', | 
|  | '-disable-opt', | 
|  | bitcode_nonfinal, '-S', '-o', bitcode]) | 
|  | shellcmd(['{bin}/pnacl-llc'.format(bin=bindir), | 
|  | '-mtriple=' + triple, | 
|  | '-externalize', | 
|  | '-filetype=obj', | 
|  | '-O2', | 
|  | '-bitcode-format=llvm', | 
|  | '-o', obj_llc, | 
|  | bitcode] + llc_flags) | 
|  | if not args.sandbox and not args.nonsfi: | 
|  | shellcmd(['{bin}/{objcopy}'.format(bin=bindir, | 
|  | objcopy=GetObjcopyCmd(args.target)), | 
|  | '--redefine-sym', '_start=_user_start', | 
|  | obj_llc | 
|  | ]) | 
|  | objs.append(obj_llc) | 
|  | if args.nonsfi: | 
|  | LinkNonsfi(objs, os.path.join(args.dir, args.output), args.target) | 
|  | elif args.sandbox: | 
|  | LinkSandbox(objs, os.path.join(args.dir, args.output), args.target) | 
|  | else: | 
|  | LinkNative(objs, os.path.join(args.dir, args.output), args.target) | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | main() |