diff --git a/llvm/projects/hpvm-tensor-rt/code_autogenerators/online_benchmark_testing_automator.py b/llvm/projects/hpvm-tensor-rt/code_autogenerators/benchmark_different_clock_frequencies_testing_automator.py similarity index 100% rename from llvm/projects/hpvm-tensor-rt/code_autogenerators/online_benchmark_testing_automator.py rename to llvm/projects/hpvm-tensor-rt/code_autogenerators/benchmark_different_clock_frequencies_testing_automator.py diff --git a/llvm/projects/hpvm-tensor-rt/code_autogenerators/source_code_autogenerator.py b/llvm/projects/hpvm-tensor-rt/code_autogenerators/source_code_autogenerator.py index 0ee30bf36d170f403a645914486e500ff3bda546..d587a3b7b57b96c8eb61b2e3e63709c7745ed466 100644 --- a/llvm/projects/hpvm-tensor-rt/code_autogenerators/source_code_autogenerator.py +++ b/llvm/projects/hpvm-tensor-rt/code_autogenerators/source_code_autogenerator.py @@ -10,6 +10,12 @@ import sys import os import re +class Approx: + FP32 = 0 + FP16 = 1 + PERF = 2 + SAMP = 3 + class KnobConfiguration: ''' Stores the configurations as well as other useful information for each knob configuration @@ -24,12 +30,45 @@ class KnobConfiguration: Args: raw_config = line of configuration file to parse ''' line_as_lst = raw_config.strip().split() + # approx,<id> knob1,knob2,etc IGNORE old_fun_name new_fun_name + + approx_id_lst = line_as_lst[0].split(',') + assert len(approx_id_lst) == 2 + + self.id = int(approx_id_lst[1]) + + if approx_id_lst[0] == "fp32": + self.approx = Approx.FP32 + return # special case + elif approx_id_lst[0] == "fp16": + self.approx = Approx.FP16 + return # special case + elif approx_id_lst[0] == "perf": + self.approx = Approx.PERF + elif approx_id_lst[0] == "samp": + self.approx = Approx.SAMP + + self.orig_func_name = line_as_lst[-2] # Second to last element + self.modified_func_name = line_as_lst[-1] # Last element + self.params = line_as_lst[1].split(",") # First element = knob configuration + + + # DEBUG + def __repr__(self): + if self.approx == Approx.FP32: + return "FP32" + elif self.approx == Approx.FP16: + return "FP16" + + approx_type = None + if self.approx == Approx.PERF: + approx_type = "PERF" + elif self.approx == Approx.SAMP: + approx_type = "SAMP" + return "Approx: %s, ID: %d, Orig func nane: %s, Modified func nane: %s, Params: %s" \ + % (approx_type, self.id, self.orig_func_name, self.modified_func_name, \ + ', '.join(self.params)) - self.id = int(line_as_lst[0]) - self.orig_func_name = line_as_lst[-2] - self.modified_func_name = line_as_lst[-1] - self.params = line_as_lst[1:-2] - def get_new_path(old_path, orig_source_code_dir): ''' @@ -71,10 +110,96 @@ def get_new_function_calls(complete_line, knob_config): new_line.append("%s%s, %s)" % (knob_config.modified_func_name, old_func_call[:-1], ', '.join(knob_config.params))) orig_func_ind = complete_line.find(knob_config.orig_func_name, line_start_ind) new_line.append(complete_line[line_start_ind : ]) - #print(new_line) return ''.join(new_line) +def convert_local_paths(file_contents, orig_source_dir): + ''' + Converts all local paths wrt the original source file's directory to paths compatible + with the current source code directory + + Args: + file_contents: String containing source code read from file + orig_source_dir: Path of original source code dir wrt the current directory + ''' + last_include_ind = file_contents.rfind("#include") + last_include_newline_ind = file_contents.find("\n", last_include_ind) + include_lines = file_contents[ : last_include_newline_ind].split("\n") + + new_file_contents = [] + for line in include_lines: + if line.startswith("#"): + include_file = line.split()[1] + if include_file.startswith("\""): + new_include_path = get_new_path(include_file.replace("\"", ""), orig_source_dir.replace("\"", "")) + new_file_contents.append("#include \"%s\"\n" % new_include_path) + else: + new_file_contents.append(line) + new_file_contents.append(file_contents[last_include_newline_ind : ]) + return '\n'.join(new_file_contents) + + +def generate_fp32_source(new_file, source_file, orig_source_dir): + # Copy the source code over + new_file_contents = convert_local_paths(source_file.read(), orig_source_dir) + new_file.write(new_file_contents) + + +def generate_fp16_source(knob_config, new_file, source_file, orig_source_dir): + file_contents = source_file.read() + + new_file_contents = convert_local_paths(file_contents, orig_source_dir) + + # Replace all tensorOperation calls with tensorHalfOperation calls + # Derived from ../bin/replace_half_calls.py + # NOTE: Not very portable but I don't see another way of ONLY replacing tensorOperation FUNCTION calls + new_file_contents = new_file_contents.replace("tensorConvolution", "tensorHalfConvolution") + new_file_contents = new_file_contents.replace("tensorAdd", "tensorHalfAdd") + new_file_contents = new_file_contents.replace("tensorRelu", "tensorHalfRelu") + new_file_contents = new_file_contents.replace("tensorRelu2", "tensorHalfRelu2") + new_file_contents = new_file_contents.replace("tensorTanh", "tensorHalfTanh") + new_file_contents = new_file_contents.replace("tensorPooling", "tensorHalfPooling") + new_file_contents = new_file_contents.replace("tensorGemmGPU", "tensorHalfGemmGPU") + + new_file.write(new_file_contents) + + +def generate_approx_source(knob_config, new_file, source_file, orig_source_dir): + new_file_contents = [] + + # Store complete line to handle cases where one line of code is split into two lines + complete_line = "" + for line in source_file: + # Replace the current path of the local include with a path that's compatible + # with the location of the generated source code + if line.startswith("#"): + include_file = line.split()[1] + if include_file.startswith("\""): + new_include_path = get_new_path(include_file.replace("\"", ""), orig_source_dir.replace("\"", "")) + new_file_contents.append("#include \"%s\"\n" % new_include_path) + else: + new_file_contents.append(line) + continue + # Handles case where 1 actual line of code is split into 2 lines + elif line.find("}") != -1 or line.find("{") != -1: + complete_line += line + new_file_contents.append(complete_line) + complete_line = "" + continue + elif line.find(";") == -1: # Last char is always \n + complete_line += line + continue + + complete_line += line + orig_func_ind = complete_line.find(knob_config.orig_func_name) + if orig_func_ind != -1: + new_file_contents.append(get_new_function_calls(complete_line, knob_config)) + else: + new_file_contents.append(complete_line) + complete_line = "" + new_file.write(''.join(new_file_contents)) + + def generate_source_code(table, dir_name, filename, source_name): ''' Generates source code for all configurations in the table for one original source @@ -84,50 +209,20 @@ def generate_source_code(table, dir_name, filename, source_name): filename: Filename of original source source_name: Filename without the file extension (ex: foo/blah.cc --> blah) ''' - file_comment = "// AUTO-GENERATED SOURCE CODE. REPLACED ALL INSTANCES OF %s WITH %s\n" - source_file = open(filename, "r") - filename_dir = os.path.dirname(filename) + orig_source_dir = os.path.dirname(filename) for knob_config in table: source_file.seek(0, 0) - - new_file_contents = [file_comment % (knob_config.orig_func_name, knob_config.modified_func_name)] - - # Store complete line to handle cases where one line of code is split into two lines - complete_line = "" - for line in source_file: - # Replace the current path of the local include with a path that's compatible - # with the location of the generated source code - if line.startswith("#"): - include_file = line.split()[1] - if include_file.startswith("\""): - new_include_path = get_new_path(include_file.replace("\"", ""), filename_dir.replace("\"", "")) - new_file_contents.append("#include \"%s\"\n" % new_include_path) - else: - new_file_contents.append(line) - continue - # Handles case where 1 actual line of code is split into 2 lines - elif line.find("}") != -1 or line.find("{") != -1: - complete_line += line - new_file_contents.append(complete_line) - complete_line = "" - continue - elif line.find(";") == -1: # Last char is always \n - complete_line += line - continue - - complete_line += line - orig_func_ind = complete_line.find(knob_config.orig_func_name) - if orig_func_ind != -1: - new_file_contents.append(get_new_function_calls(complete_line, knob_config)) - else: - new_file_contents.append(complete_line) - complete_line = "" - new_filename = os.path.join(dir_name, "%s_%s.cc" % (source_name, knob_config.id)) new_file = open(new_filename, "w") - new_file.write(''.join(new_file_contents)) + if knob_config.approx == Approx.FP16: + generate_fp16_source(knob_config, new_file, source_file, orig_source_dir) + elif knob_config.approx == Approx.FP32: + generate_fp32_source(new_file, source_file, orig_source_dir) + else: + generate_approx_source(knob_config, new_file, source_file, orig_source_dir) + new_file.close() print("Generated source code as %s" % new_filename) source_file.close()