Index: llvm/utils/llvm-mca-pretty-printer/llvm-mca-pretty-printer.py =================================================================== --- /dev/null +++ llvm/utils/llvm-mca-pretty-printer/llvm-mca-pretty-printer.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python + +import argparse +import os +import sys +import platform +import array as arr +from json import loads +from subprocess import Popen, PIPE +from matplotlib.cm import get_cmap + +# Holds a file statistics. +class OneFileStats: + def __init__(self, Name, BlockRThroughput, DispatchWidth, + IPC, Instructions, Iterations, TotalCycles, TotaluOps, uOpsPerCycle): + self.Name = Name + self.BlockRThroughput = BlockRThroughput + self.DispatchWidth = DispatchWidth + self.IPC = IPC + self.Instructions = Instructions + self.Iterations = Iterations + self.TotalCycles = TotalCycles + self.TotaluOps = TotaluOps + self.uOpsPerCycle = uOpsPerCycle + +# Parse the program arguments. +def parse_program_args(parser): + parser.add_argument('file_names', nargs = '+', type = str, help = 'Names of files which tool process.') + parser.add_argument('-mtriple', nargs = 1, type = str, action = 'store', default = ['x86_64-unknown-linux-gnu'], help = 'Target triple.') + parser.add_argument('-mcpu', nargs = 1, type = str, action = 'store', default = ['skylake'], help = 'Target a specific cpu type .') + parser.add_argument('-diff', action = 'store_true', default = False, help = 'Prints the difference of statistics for multiple input assembler files.') + parser.add_argument('-iterations', type = int, nargs = 1, action = 'store', default = [100], help = 'Number of iterations to run.') + parser.add_argument('-useMca', nargs = 1, type = str, action = 'store', help = 'Specified relative path to llvm-mca.') + return parser.parse_args() + +# Verify that the program inputs meet the requirements. +def verify_program_inputs(opts): + if len(sys.argv) < 2 : + print ('error: Wrong number of arguments.') + return False + if opts.diff and len(opts.file_names) < 2: + print ('error: Wrong number of input files.') + return False + if not opts.diff and len(opts.file_names) > 1: + print ('error: Wrong number of input files.') + return False + return True + +# Returns the name of the file to be analyzed from the path it is on. +def getFilenameFromPath (path): + indexOfSlash = path.rfind("/") + return path[(indexOfSlash + 1) : len(path)] + +# Returns the results of the running llvm-mca tool for the input file. +def llvmMcaStats (opts, FileName): + + # Get the directory of the LLVM tools. + if not opts.useMca: + llvm_mca_cmd = os.path.normpath(os.path.join(os.path.dirname(__file__), "../../../../build/bin/llvm-mca")) + else: + llvm_mca_cmd = opts.useMca[0] + + # The statistics llvm-mca options. + llvm_mca_stats_opt1 = "-mtriple=" + opts.mtriple[0] + llvm_mca_stats_opt2 = "-mcpu=" + opts.mcpu[0] + llvm_mca_stats_opt3 = "-json" + llvm_mca_stats_opt4 = "-iterations=" + str(opts.iterations[0]) + + # Generate the stats with the llvm-mca. + subproc = Popen([llvm_mca_cmd, llvm_mca_stats_opt1, llvm_mca_stats_opt2, \ + llvm_mca_stats_opt3, llvm_mca_stats_opt4, FileName], \ + stdin = PIPE, stdout = PIPE, stderr = PIPE, \ + universal_newlines = True) + cmd_stdout, cmd_stderr = subproc.communicate() + + try: + json_parsed = loads(cmd_stdout) + except: + print ('error: No valid llvm-mca statistics found.') + print(cmd_stderr) + sys.exit(1) + + FileStats = OneFileStats(opts.file_names[0], \ + json_parsed["CodeRegions"][0]["SummaryView"]["BlockRThroughput"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["DispatchWidth"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["IPC"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["Instructions"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["Iterations"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["TotalCycles"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["TotaluOps"], \ + json_parsed["CodeRegions"][0]["SummaryView"]["uOpsPerCycle"]) + + return FileStats + +# Print statistics in console for single file or for multiple files. +def printResult (arrayOneFileStats, opts): + import termtables as tt + + array = [None] * (len(opts.file_names) + 1) + array[0] = " " + + print ("Input files:") + for i in range (len(arrayOneFileStats)): + print("[f" + str(i + 1) + "]: " + getFilenameFromPath(opts.file_names[i]) + "\n") + array[i + 1] = "[f" + str(i + 1) + "]: " + + print("ITERATIONS: " + str(arrayOneFileStats[0].Iterations) + "\n") + + matrix = [ [ [None] for i in range(len(arrayOneFileStats) + 1) ] for j in range(7) ] + + matrix[0][0] = "Instructions: " + matrix[1][0] = "Total Cycles: " + matrix[2][0] = "Total uOps: " + matrix[3][0] = "Dispatch Width: " + matrix[4][0] = "uOps Per Cycle: " + matrix[5][0] = "IPC: " + matrix[6][0] = "Block RThroughp: " + + for j in range (len(arrayOneFileStats)): + matrix[0][j + 1] = str(arrayOneFileStats[j].Instructions) + matrix[1][j + 1] = str(arrayOneFileStats[j].TotalCycles) + matrix[2][j + 1] = str(arrayOneFileStats[j].TotaluOps) + matrix[3][j + 1] = str(arrayOneFileStats[j].DispatchWidth) + matrix[4][j + 1] = str(round(arrayOneFileStats[j].uOpsPerCycle, 2)) + matrix[5][j + 1] = str(round(arrayOneFileStats[j].IPC, 2)) + matrix[6][j + 1] = str(round(arrayOneFileStats[j].BlockRThroughput, 2)) + + tt.print(matrix, header = array, style = tt.styles.ascii_thin_double, padding = (0, 1)) + +def Main(): + parser = argparse.ArgumentParser() + opts = parse_program_args(parser) + + if not verify_program_inputs(opts): + parser.print_help() + sys.exit(1) + + arrayOneFileStats = [None] * len(opts.file_names) + + if opts.diff: + for i in range (len(opts.file_names)): + arrayOneFileStats[i] = llvmMcaStats(opts, opts.file_names[i]) + printResult(arrayOneFileStats, opts) + else: + arrayOneFileStats[0] = llvmMcaStats(opts, opts.file_names[0]) + printResult(arrayOneFileStats, opts) + +if __name__ == '__main__': + Main() + sys.exit(0)