| #!/usr/bin/env python2 |
| |
| import argparse |
| import itertools |
| import re |
| |
| if __name__ == '__main__': |
| """Compares a LLVM file with a subzero file for differences. |
| |
| Before comparing, the LLVM file is massaged to remove comments, |
| blank lines, global variable definitions, external function |
| declarations, and possibly other patterns that llvm2ice does not |
| handle. |
| |
| The subzero file and the massaged LLVM file are compared line by |
| line for differences. However, there is a regex defined such that |
| if the regex matches a line in the LLVM file, that line and the |
| corresponding line in the subzero file are ignored. This lets us |
| ignore minor differences such as inttoptr and ptrtoint, and |
| printing of floating-point constants. |
| |
| On success, no output is produced. On failure, each mismatch is |
| printed as two lines, one starting with 'SZ' (subzero) and one |
| starting with 'LL' (LLVM). |
| """ |
| desc = 'Compare LLVM and subzero bitcode files.' |
| argparser = argparse.ArgumentParser(description=desc) |
| argparser.add_argument( |
| 'llfile', nargs=1, |
| type=argparse.FileType('r'), metavar='LLVM_FILE', |
| help='LLVM bitcode file') |
| argparser.add_argument( |
| 'szfile', nargs='?', default='-', |
| type=argparse.FileType('r'), metavar='SUBZERO_FILE', |
| help='Subzero bitcode file [default stdin]') |
| args = argparser.parse_args() |
| bitcode = args.llfile[0].readlines() |
| sz_out = [ line.rstrip() for line in args.szfile.readlines()] |
| |
| # Filter certain lines and patterns from the input, and collect |
| # the remainder into llc_out. |
| llc_out = [] |
| tail_call = re.compile(' tail call '); |
| trailing_comment = re.compile(';.*') |
| ignore_pattern = re.compile('|'.join([ |
| '^ *$', # all-whitespace lines |
| '^declare', # declarations without definitions |
| '^@.*\]$' # PNaCl global declarations like: |
| # @v = external global [4 x i8] |
| ])) |
| prev_line = None |
| for line in bitcode: |
| if prev_line: |
| line = prev_line + line |
| prev_line = None |
| # Convert tail call into regular (non-tail) call. |
| line = tail_call.sub(' call ', line) |
| # Remove trailing comments and spaces. |
| line = trailing_comment.sub('', line).rstrip() |
| # Ignore blanks lines, forward declarations, and variable definitions. |
| if ignore_pattern.search(line): |
| continue |
| # SZ doesn't break up long lines, but LLVM does. Normalize to SZ. |
| if line.endswith(','): |
| prev_line = line |
| continue |
| llc_out.append(line) |
| |
| # Compare sz_out and llc_out line by line, but ignore pairs of |
| # lines where the llc line matches a certain pattern. |
| return_code = 0 |
| lines_total = 0 |
| lines_diff = 0 |
| ignore_pattern = re.compile( |
| '|'.join(['[ (](float|double) [-0-9]', # FP constants |
| '[ (](float|double) %\w+, [-0-9]', |
| ' @llvm\..*i\d+\*', # intrinsic calls w/ pointer args |
| ' i\d+\* @llvm\.', # intrinsic calls w/ pointer ret |
| ' inttoptr ', # inttoptr pointer types |
| ' ptrtoint ', # ptrtoint pointer types |
| ' bitcast .*\* .* to .*\*' # bitcast pointer types |
| ])) |
| for (sz_line, llc_line) in itertools.izip_longest(sz_out, llc_out): |
| lines_total += 1 |
| if sz_line == llc_line: |
| continue |
| if llc_line and ignore_pattern.search(llc_line): |
| lines_diff += 1 |
| continue |
| if sz_line: print 'SZ (%d)> %s' % (lines_total, sz_line) |
| if llc_line: print 'LL (%d)> %s' % (lines_total, llc_line) |
| return_code = 1 |
| |
| if return_code == 0: |
| message = 'Success (ignored %d diffs out of %d lines)' |
| print message % (lines_diff, lines_total) |
| exit(return_code) |