def binary(self, output, objects, options=None, link_main=True, main_options=None): assert self.target is not None, ( "must specify target= to constructor, or compile sources which specify the target " "first") args = [self._autodetect_toolchain_prefix(self.target) + "g++"] args.extend(self._defaults_from_target(self.target)) if options is not None: args.extend(options.get("ldflags", [])) for include_dir in options.get("include_dirs", []): args.extend(["-I", include_dir]) output_filename = os.path.basename(output) output_abspath = os.path.join(output, output_filename) args.extend(["-g", "-o", output_abspath]) if link_main: host_main_srcs = glob.glob( os.path.join(build.CRT_ROOT_DIR, "host", "*.cc")) if main_options: main_lib = self.library(os.path.join(output, "host"), host_main_srcs, main_options) for lib_name in main_lib.library_files: args.append(main_lib.abspath(lib_name)) else: args.extend(host_main_srcs) for obj in objects: for lib_name in obj.library_files: args.append(obj.abspath(lib_name)) binutil.run_cmd(args) return tvm.micro.MicroBinary(output, output_filename, [])
def library(self, output, sources, options=None): options = options if options is not None else {} try: target = self._target_from_sources(sources) except DetectTargetError: assert self.target is not None, ( "Must specify target= to constructor when compiling sources which don't specify a " "target") target = self.target if self.target is not None and str(self.target) != str(target): raise IncompatibleTargetError( f"auto-detected target {target} differs from configured {self.target}" ) prefix = self._autodetect_toolchain_prefix(target) outputs = [] for src in sources: src_base, src_ext = os.path.splitext(os.path.basename(src)) compiler_name = {".c": "gcc", ".cc": "g++", ".cpp": "g++"}[src_ext] args = [prefix + compiler_name, "-g"] args.extend(self._defaults_from_target(target)) args.extend(options.get(f"{src_ext[1:]}flags", [])) for include_dir in options.get("include_dirs", []): args.extend(["-I", include_dir]) output_filename = f"{src_base}.o" output_abspath = os.path.join(output, output_filename) binutil.run_cmd(args + ["-c", "-o", output_abspath, src]) outputs.append(output_abspath) output_filename = f"{os.path.basename(output)}.a" output_abspath = os.path.join(output, output_filename) binutil.run_cmd([prefix + "ar", "-r", output_abspath] + outputs) binutil.run_cmd([prefix + "ranlib", output_abspath]) return tvm.micro.MicroLibrary(output, [output_filename])
def create_micro_lib_base( out_obj_path, in_src_path, toolchain_prefix, device_id, lib_type, options=None, lib_src_paths=None, ): """Compiles code into a binary for the target micro device. Parameters ---------- out_obj_path : str path to generated object file in_src_path : str path to source file toolchain_prefix : str toolchain prefix to be used. For example, a prefix of "riscv64-unknown-elf-" means "riscv64-unknown-elf-gcc" is used as the compiler and "riscv64-unknown-elf-ld" is used as the linker, etc. device_id : str unique identifier for the target device lib_type : micro.LibType whether to compile a MicroTVM runtime or operator library options : List[str] additional options to pass to GCC lib_src_paths : Optional[List[str]] paths to additional source files to be compiled into the library """ # look at these (specifically `strip`): # https://stackoverflow.com/questions/15314581/g-compiler-flag-to-minimize-binary-size base_compile_cmd = [ f"{toolchain_prefix}gcc", "-std=gnu11", "-Wall", "-Wextra", "--pedantic", "-c", "-g", "-nostartfiles", "-nodefaultlibs", "-nostdlib", "-fdata-sections", "-ffunction-sections", ] if options is not None: base_compile_cmd += options src_paths = [] include_paths = find_include_path() + [get_micro_host_driven_dir()] tmp_dir = _util.tempdir() # we need to create a new src file in the operator branch new_in_src_path = in_src_path if lib_type == LibType.RUNTIME: dev_dir = _get_device_source_dir(device_id) print(dev_dir) dev_src_paths = glob.glob(f"{dev_dir}/*.[csS]") # there needs to at least be a utvm_timer.c file assert dev_src_paths assert "utvm_timer.c" in map(os.path.basename, dev_src_paths) src_paths += dev_src_paths elif lib_type == LibType.OPERATOR: # create a temporary copy of the operator source, so we can inject the dev lib # header without modifying the original. temp_src_path = tmp_dir.relpath("temp.c") with open(in_src_path, "r") as f: src_lines = f.read().splitlines() src_lines.insert(0, '#include "utvm_device_dylib_redirect.c"') with open(temp_src_path, "w") as f: f.write("\n".join(src_lines)) new_in_src_path = temp_src_path else: raise RuntimeError("unknown lib type") src_paths += [new_in_src_path] # add any src paths required by the operator if lib_src_paths is not None: src_paths += lib_src_paths # print(f"include paths: {include_paths}") for path in include_paths: base_compile_cmd += ["-I", path] prereq_obj_paths = [] # print(src_paths) for src_path in src_paths: curr_obj_path = tmp_dir.relpath( pathlib.Path(src_path).with_suffix(".o").name) assert curr_obj_path not in prereq_obj_paths prereq_obj_paths.append(curr_obj_path) curr_compile_cmd = base_compile_cmd + [src_path, "-o", curr_obj_path] # TODO(weberlo): make compilation fail if there are any warnings run_cmd(curr_compile_cmd) ld_cmd = [f"{toolchain_prefix}ld", "-relocatable"] ld_cmd += prereq_obj_paths ld_cmd += ["-o", out_obj_path] run_cmd(ld_cmd)
def create_micro_lib_base(out_obj_path, in_src_path, toolchain_prefix, device_id, lib_type, options=None): """Compiles code into a binary for the target micro device. Parameters ---------- out_obj_path : str path to generated object file in_src_path : str path to source file toolchain_prefix : str toolchain prefix to be used. For example, a prefix of "riscv64-unknown-elf-" means "riscv64-unknown-elf-gcc" is used as the compiler and "riscv64-unknown-elf-ld" is used as the linker, etc. device_id : str unique identifier for the target device lib_type : micro.LibType whether to compile a MicroTVM runtime or operator library options : List[str] additional options to pass to GCC """ base_compile_cmd = [ f"{toolchain_prefix}gcc", "-std=c11", "-Wall", "-Wextra", "--pedantic", "-c", "-O0", "-g", "-nostartfiles", "-nodefaultlibs", "-nostdlib", "-fdata-sections", "-ffunction-sections", ] if options is not None: base_compile_cmd += options src_paths = [] include_paths = find_include_path() + [get_micro_host_driven_dir()] tmp_dir = _util.tempdir() # we might transform the src path in one of the branches below new_in_src_path = in_src_path if lib_type == LibType.RUNTIME: dev_dir = _get_device_source_dir(device_id) dev_src_paths = glob.glob(f"{dev_dir}/*.[csS]") # there needs to at least be a utvm_timer.c file assert dev_src_paths assert "utvm_timer.c" in map(os.path.basename, dev_src_paths) src_paths += dev_src_paths elif lib_type == LibType.OPERATOR: # create a temporary copy of the source, so we can inject the dev lib # header without modifying the original. temp_src_path = tmp_dir.relpath("temp.c") with open(in_src_path, "r") as f: src_lines = f.read().splitlines() src_lines.insert(0, "#include \"utvm_device_dylib_redirect.c\"") with open(temp_src_path, "w") as f: f.write("\n".join(src_lines)) new_in_src_path = temp_src_path base_compile_cmd += ["-c"] else: raise RuntimeError("unknown lib type") src_paths += [new_in_src_path] for path in include_paths: base_compile_cmd += ["-I", path] prereq_obj_paths = [] for src_path in src_paths: curr_obj_path = Path(src_path).with_suffix(".o").name assert curr_obj_path not in prereq_obj_paths prereq_obj_paths.append(curr_obj_path) curr_compile_cmd = base_compile_cmd + [src_path, "-o", curr_obj_path] run_cmd(curr_compile_cmd) ld_cmd = [f"{toolchain_prefix}ld", "-relocatable"] ld_cmd += prereq_obj_paths ld_cmd += ["-o", out_obj_path] run_cmd(ld_cmd)