1#!/usr/bin/env python2 2 3import argparse 4import os 5import subprocess 6import sys 7import tempfile 8 9import targets 10from szbuild import LinkNonsfi 11from utils import FindBaseNaCl, GetObjcopyCmd, get_sfi_string, shellcmd 12 13def main(): 14 """Builds a cross-test binary for comparing Subzero and llc translation. 15 16 Each --test argument is compiled once by llc and once by Subzero. C/C++ 17 tests are first compiled down to PNaCl bitcode using pnacl-clang and 18 pnacl-opt. The --prefix argument ensures that symbol names are different 19 between the two object files, to avoid linking errors. 20 21 There is also a --driver argument that specifies the C/C++ file that calls 22 the test functions with a variety of interesting inputs and compares their 23 results. 24 25 """ 26 # arch_map maps a Subzero target string to TargetInfo (e.g., triple). 27 arch_map = { 'x8632': targets.X8632Target, 28 'x8664': targets.X8664Target, 29 'arm32': targets.ARM32Target, 30 'mips32': targets.MIPS32Target} 31 arch_sz_flags = { 'x8632': [], 32 'x8664': [], 33 # For ARM, test a large stack offset as well. +/- 4095 is 34 # the limit, so test somewhere near that boundary. 35 'arm32': ['--test-stack-extra', '4084'], 36 'mips32': ['--test-stack-extra', '4084'] 37 } 38 arch_llc_flags_extra = { 39 # Use sse2 instructions regardless of input -mattr 40 # argument to avoid differences in (undefined) behavior of 41 # converting NaN to int. 42 'x8632': ['-mattr=sse2'], 43 'x8664': ['-mattr=sse2'], 44 'arm32': [], 45 'mips32':[], 46 } 47 desc = 'Build a cross-test that compares Subzero and llc translation.' 48 argparser = argparse.ArgumentParser(description=desc) 49 argparser.add_argument('--test', required=True, action='append', 50 metavar='TESTFILE_LIST', 51 help='List of C/C++/.ll files with test functions') 52 argparser.add_argument('--driver', required=True, 53 metavar='DRIVER', 54 help='Driver program') 55 argparser.add_argument('--target', required=False, default='x8632', 56 choices=arch_map.keys(), 57 metavar='TARGET', 58 help='Translation target architecture.' + 59 ' Default %(default)s.') 60 argparser.add_argument('-O', required=False, default='2', dest='optlevel', 61 choices=['m1', '-1', '0', '1', '2'], 62 metavar='OPTLEVEL', 63 help='Optimization level for llc and Subzero ' + 64 '(m1 and -1 are equivalent).' + 65 ' Default %(default)s.') 66 argparser.add_argument('--clang-opt', required=False, default=True, 67 dest='clang_opt') 68 argparser.add_argument('--mattr', required=False, default='sse2', 69 dest='attr', choices=['sse2', 'sse4.1', 70 'neon', 'hwdiv-arm', 71 'base'], 72 metavar='ATTRIBUTE', 73 help='Target attribute. Default %(default)s.') 74 argparser.add_argument('--sandbox', required=False, default=0, type=int, 75 dest='sandbox', 76 help='Use sandboxing. Default "%(default)s".') 77 argparser.add_argument('--nonsfi', required=False, default=0, type=int, 78 dest='nonsfi', 79 help='Use Non-SFI mode. Default "%(default)s".') 80 argparser.add_argument('--prefix', required=True, 81 metavar='SZ_PREFIX', 82 help='String prepended to Subzero symbol names') 83 argparser.add_argument('--output', '-o', required=True, 84 metavar='EXECUTABLE', 85 help='Executable to produce') 86 argparser.add_argument('--dir', required=False, default='.', 87 metavar='OUTPUT_DIR', 88 help='Output directory for all files.' + 89 ' Default "%(default)s".') 90 argparser.add_argument('--filetype', default='obj', dest='filetype', 91 choices=['obj', 'asm', 'iasm'], 92 help='Output file type. Default %(default)s.') 93 argparser.add_argument('--sz', dest='sz_args', action='append', default=[], 94 help='Extra arguments to pass to pnacl-sz.') 95 args = argparser.parse_args() 96 97 nacl_root = FindBaseNaCl() 98 bindir = ('{root}/toolchain/linux_x86/pnacl_newlib_raw/bin' 99 .format(root=nacl_root)) 100 target_info = arch_map[args.target] 101 triple = target_info.triple 102 if args.sandbox: 103 triple = targets.ConvertTripleToNaCl(triple) 104 llc_flags = target_info.llc_flags + arch_llc_flags_extra[args.target] 105 if args.nonsfi: 106 llc_flags.extend(['-relocation-model=pic', 107 '-malign-double', 108 '-force-tls-non-pic', 109 '-mtls-use-call']) 110 mypath = os.path.abspath(os.path.dirname(sys.argv[0])) 111 112 # Construct a "unique key" for each test so that tests can be run in 113 # parallel without race conditions on temporary file creation. 114 key = '{sb}.O{opt}.{attr}.{target}'.format( 115 target=args.target, 116 sb=get_sfi_string(args, 'sb', 'nonsfi', 'nat'), 117 opt=args.optlevel, attr=args.attr) 118 objs = [] 119 for arg in args.test: 120 base, ext = os.path.splitext(arg) 121 if ext == '.ll': 122 bitcode = arg 123 else: 124 # Use pnacl-clang and pnacl-opt to produce PNaCl bitcode. 125 bitcode_nonfinal = os.path.join(args.dir, base + '.' + key + '.bc') 126 bitcode = os.path.join(args.dir, base + '.' + key + '.pnacl.ll') 127 shellcmd(['{bin}/pnacl-clang'.format(bin=bindir), 128 ('-O2' if args.clang_opt else '-O0'), 129 ('-DARM32' if args.target == 'arm32' else ''), '-c', arg, 130 ('-DMIPS32' if args.target == 'mips32' else ''), 131 '-o', bitcode_nonfinal]) 132 shellcmd(['{bin}/pnacl-opt'.format(bin=bindir), 133 '-pnacl-abi-simplify-preopt', 134 '-pnacl-abi-simplify-postopt', 135 '-pnaclabi-allow-debug-metadata', 136 '-strip-metadata', 137 '-strip-module-flags', 138 '-strip-debug', 139 bitcode_nonfinal, '-S', '-o', bitcode]) 140 141 base_sz = '{base}.{key}'.format(base=base, key=key) 142 asm_sz = os.path.join(args.dir, base_sz + '.sz.s') 143 obj_sz = os.path.join(args.dir, base_sz + '.sz.o') 144 obj_llc = os.path.join(args.dir, base_sz + '.llc.o') 145 146 shellcmd(['{path}/pnacl-sz'.format(path=os.path.dirname(mypath)), 147 ] + args.sz_args + [ 148 '-O' + args.optlevel, 149 '-mattr=' + args.attr, 150 '--target=' + args.target, 151 '--sandbox=' + str(args.sandbox), 152 '--nonsfi=' + str(args.nonsfi), 153 '--prefix=' + args.prefix, 154 '-allow-uninitialized-globals', 155 '-externalize', 156 '-filetype=' + args.filetype, 157 '-o=' + (obj_sz if args.filetype == 'obj' else asm_sz), 158 bitcode] + arch_sz_flags[args.target]) 159 if args.filetype != 'obj': 160 shellcmd(['{bin}/llvm-mc'.format(bin=bindir), 161 '-triple=' + ('mipsel-linux-gnu' 162 if args.target == 'mips32' and args.sandbox 163 else triple), 164 '-filetype=obj', 165 '-o=' + obj_sz, 166 asm_sz]) 167 168 # Each separately translated Subzero object file contains its own 169 # definition of the __Sz_block_profile_info profiling symbol. Avoid 170 # linker errors (multiply defined symbol) by making all copies weak. 171 # (This could also be done by Subzero if it supported weak symbol 172 # definitions.) This approach should be OK because cross tests are 173 # currently the only situation where multiple translated files are 174 # linked into the executable, but when PNaCl supports shared nexe 175 # libraries, this would need to change. (Note: the same issue applies 176 # to the __Sz_revision symbol.) 177 shellcmd(['{bin}/{objcopy}'.format(bin=bindir, 178 objcopy=GetObjcopyCmd(args.target)), 179 '--weaken-symbol=__Sz_block_profile_info', 180 '--weaken-symbol=__Sz_revision', 181 '--strip-symbol=nacl_tp_tdb_offset', 182 '--strip-symbol=nacl_tp_tls_offset', 183 obj_sz]) 184 objs.append(obj_sz) 185 shellcmd(['{bin}/pnacl-llc'.format(bin=bindir), 186 '-mtriple=' + triple, 187 '-externalize', 188 '-filetype=obj', 189 '-bitcode-format=llvm', 190 '-o=' + obj_llc, 191 bitcode] + llc_flags) 192 strip_syms = [] if args.target == 'mips32' else ['nacl_tp_tdb_offset', 193 'nacl_tp_tls_offset'] 194 shellcmd(['{bin}/{objcopy}'.format(bin=bindir, 195 objcopy=GetObjcopyCmd(args.target)), 196 obj_llc] + 197 [('--strip-symbol=' + sym) for sym in strip_syms]) 198 objs.append(obj_llc) 199 200 # Add szrt_sb_${target}.o or szrt_native_${target}.o. 201 if not args.nonsfi: 202 objs.append(( 203 '{root}/toolchain_build/src/subzero/build/runtime/' + 204 'szrt_{sb}_' + args.target + '.o' 205 ).format(root=nacl_root, 206 sb=get_sfi_string(args, 'sb', 'nonsfi', 'native'))) 207 208 target_params = [] 209 210 if args.target == 'arm32': 211 target_params.append('-DARM32') 212 target_params.append('-static') 213 214 if args.target == 'mips32': 215 target_params.append('-DMIPS32') 216 217 pure_c = os.path.splitext(args.driver)[1] == '.c' 218 if not args.nonsfi: 219 # Set compiler to clang, clang++, pnacl-clang, or pnacl-clang++. 220 compiler = '{bin}/{prefix}{cc}'.format( 221 bin=bindir, prefix=get_sfi_string(args, 'pnacl-', '', ''), 222 cc='clang' if pure_c else 'clang++') 223 sb_native_args = (['-O0', '--pnacl-allow-native', 224 '-arch', target_info.compiler_arch, 225 '-Wn,-defsym=__Sz_AbsoluteZero=0'] 226 if args.sandbox else 227 ['-g', '-target=' + triple, 228 '-lm', '-lpthread', 229 '-Wl,--defsym=__Sz_AbsoluteZero=0'] + 230 target_info.cross_headers) 231 shellcmd([compiler] + target_params + [args.driver] + objs + 232 ['-o', os.path.join(args.dir, args.output)] + sb_native_args) 233 return 0 234 235 base, ext = os.path.splitext(args.driver) 236 bitcode_nonfinal = os.path.join(args.dir, base + '.' + key + '.bc') 237 bitcode = os.path.join(args.dir, base + '.' + key + '.pnacl.ll') 238 asm_sz = os.path.join(args.dir, base + '.' + key + '.s') 239 obj_llc = os.path.join(args.dir, base + '.' + key + '.o') 240 compiler = '{bin}/{prefix}{cc}'.format( 241 bin=bindir, prefix='pnacl-', 242 cc='clang' if pure_c else 'clang++') 243 shellcmd([compiler] + target_params + [ 244 args.driver, 245 '-O2', 246 '-o', bitcode_nonfinal, 247 '-Wl,-r' 248 ]) 249 shellcmd(['{bin}/pnacl-opt'.format(bin=bindir), 250 '-pnacl-abi-simplify-preopt', 251 '-pnacl-abi-simplify-postopt', 252 '-pnaclabi-allow-debug-metadata', 253 '-strip-metadata', 254 '-strip-module-flags', 255 '-strip-debug', 256 '-disable-opt', 257 bitcode_nonfinal, '-S', '-o', bitcode]) 258 shellcmd(['{bin}/pnacl-llc'.format(bin=bindir), 259 '-mtriple=' + triple, 260 '-externalize', 261 '-filetype=obj', 262 '-O2', 263 '-bitcode-format=llvm', 264 '-o', obj_llc, 265 bitcode] + llc_flags) 266 if not args.sandbox and not args.nonsfi: 267 shellcmd(['{bin}/{objcopy}'.format(bin=bindir, 268 objcopy=GetObjcopyCmd(args.target)), 269 '--redefine-sym', '_start=_user_start', 270 obj_llc 271 ]) 272 objs.append(obj_llc) 273 if args.nonsfi: 274 LinkNonsfi(objs, os.path.join(args.dir, args.output), args.target) 275 elif args.sandbox: 276 LinkSandbox(objs, os.path.join(args.dir, args.output), args.target) 277 else: 278 LinkNative(objs, os.path.join(args.dir, args.output), args.target) 279 280if __name__ == '__main__': 281 main() 282