1# Licensed to the Apache Software Foundation (ASF) under one
2# or more contributor license agreements.  See the NOTICE file
3# distributed with this work for additional information
4# regarding copyright ownership.  The ASF licenses this file
5# to you under the Apache License, Version 2.0 (the
6# "License"); you may not use this file except in compliance
7# with the License.  You may obtain a copy of the License at
8#
9#   http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing,
12# software distributed under the License is distributed on an
13# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14# KIND, either express or implied.  See the License for the
15# specific language governing permissions and limitations
16# under the License.
17
18"""Defines top-level glue functions for building microTVM artifacts."""
19
20import copy
21import logging
22import os
23import re
24from tvm.contrib import util
25
26
27_LOG = logging.getLogger(__name__)
28
29
30class Workspace:
31    """Defines helper functions for manipulating temporary compilation workspaces."""
32
33    def __init__(self, root=None, debug=False):
34        if debug or root is not None:
35            with util.TempDirectory.set_keep_for_debug():
36                self.tempdir = util.tempdir(custom_path=root)
37                _LOG.info("Created debug mode workspace at: %s", self.tempdir.temp_dir)
38        else:
39            self.tempdir = util.tempdir()
40
41    def relpath(self, path):
42        return self.tempdir.relpath(path)
43
44    def listdir(self):
45        return self.tempdir.listdir()
46
47    @property
48    def path(self):
49        return self.tempdir.temp_dir
50
51
52# Required C runtime libraries, in link order.
53CRT_RUNTIME_LIB_NAMES = ["utvm_rpc_server", "utvm_rpc_common", "common"]
54
55
56TVM_ROOT_DIR = os.path.realpath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
57
58
59CRT_ROOT_DIR = os.path.join(TVM_ROOT_DIR, "src", "runtime", "crt")
60
61
62RUNTIME_LIB_SRC_DIRS = [os.path.join(CRT_ROOT_DIR, n) for n in CRT_RUNTIME_LIB_NAMES] + [
63    os.path.join(TVM_ROOT_DIR, "3rdparty/libcrc/src")
64]
65
66
67RUNTIME_SRC_REGEX = re.compile(r"^.*\.cc?$", re.IGNORECASE)
68
69
70_CRT_DEFAULT_OPTIONS = {
71    "ccflags": ["-std=c++11"],
72    "ldflags": ["-std=gnu++14"],
73    "include_dirs": [
74        f"{TVM_ROOT_DIR}/include",
75        f"{TVM_ROOT_DIR}/3rdparty/dlpack/include",
76        f"{TVM_ROOT_DIR}/3rdparty/libcrc/include",
77        f"{TVM_ROOT_DIR}/3rdparty/dmlc-core/include",
78        f"{CRT_ROOT_DIR}/include",
79    ],
80    "profile": {"common": ["-Wno-unused-variable"]},
81}
82
83
84def default_options(target_include_dir):
85    """Return default opts passed to Compile commands."""
86    bin_opts = copy.deepcopy(_CRT_DEFAULT_OPTIONS)
87    bin_opts["include_dirs"].append(target_include_dir)
88    lib_opts = copy.deepcopy(bin_opts)
89    lib_opts["profile"]["common"].append("-Werror")
90    lib_opts["cflags"] = ["-Wno-error=incompatible-pointer-types"]
91    return {"bin_opts": bin_opts, "lib_opts": lib_opts}
92
93
94def build_static_runtime(workspace, compiler, module, lib_opts=None, bin_opts=None):
95    """Build the on-device runtime, statically linking the given modules.
96
97    Parameters
98    ----------
99    compiler : tvm.micro.Compiler
100        Compiler instance used to build the runtime.
101
102    module : IRModule
103        Module to statically link.
104
105    lib_opts : dict
106        Extra kwargs passed to library(),
107
108    bin_opts : dict
109        Extra kwargs passed to binary(),
110
111    Returns
112    -------
113    MicroBinary :
114        The compiled runtime.
115    """
116    lib_opts = _CRT_DEFAULT_OPTIONS if lib_opts is None else lib_opts
117    bin_opts = _CRT_DEFAULT_OPTIONS if bin_opts is None else bin_opts
118
119    mod_build_dir = workspace.relpath(os.path.join("build", "module"))
120    os.makedirs(mod_build_dir)
121    mod_src_dir = workspace.relpath(os.path.join("src", "module"))
122    os.makedirs(mod_src_dir)
123    mod_src_path = os.path.join(mod_src_dir, "module.c")
124    module.save(mod_src_path, "cc")
125
126    libs = []
127    for lib_src_dir in RUNTIME_LIB_SRC_DIRS:
128        lib_name = os.path.basename(lib_src_dir)
129        lib_build_dir = workspace.relpath(f"build/{lib_name}")
130        os.makedirs(lib_build_dir)
131
132        lib_srcs = []
133        for p in os.listdir(lib_src_dir):
134            if RUNTIME_SRC_REGEX.match(p):
135                lib_srcs.append(os.path.join(lib_src_dir, p))
136
137        libs.append(compiler.library(lib_build_dir, lib_srcs, lib_opts))
138
139    libs.append(compiler.library(mod_build_dir, [mod_src_path], lib_opts))
140
141    runtime_build_dir = workspace.relpath(f"build/runtime")
142    os.makedirs(runtime_build_dir)
143    return compiler.binary(runtime_build_dir, libs, bin_opts)
144