diff --git a/python/tvm/contrib/binutil.py b/python/tvm/contrib/binutil.py index b6c369bf2e2b..a444cdc0495e 100644 --- a/python/tvm/contrib/binutil.py +++ b/python/tvm/contrib/binutil.py @@ -38,8 +38,8 @@ def tvm_callback_get_section_size(binary_path, section_name, toolchain_prefix): toolchain_prefix : str prefix for binary names in target compiler toolchain - Return - ------ + Returns + ------- size : integer size of the section in bytes """ @@ -61,8 +61,8 @@ def tvm_callback_get_section_size(binary_path, section_name, toolchain_prefix): section_mapping = { ".text": [".text"], ".rodata": [".rodata"], - ".data": [".data"], - ".bss": [".bss", ".sbss", ".sdata"], + ".data": [".data", ".sdata"], + ".bss": [".bss", ".sbss"], } sections_to_sum = section_mapping["." + section_name] section_size = 0 @@ -103,8 +103,8 @@ def tvm_callback_relocate_binary( toolchain_prefix : str prefix for binary names in target compiler toolchain - Return - ------ + Returns + ------- rel_bin : bytearray the relocated binary """ @@ -114,8 +114,6 @@ def tvm_callback_relocate_binary( # TODO(weberlo): There should be a better way to configure this for different archs. if "riscv" in toolchain_prefix: ld_script_contents += "OUTPUT_ARCH( \"riscv\" )\n\n" - # TODO(weberlo): *Should* ".sdata" and ".sbss" be linked into the ".bss" - # section? # TODO(weberlo): Generate the script in a more procedural manner. ld_script_contents += """ SECTIONS @@ -143,6 +141,8 @@ def tvm_callback_relocate_binary( *(.data) . = ALIGN(8); *(.data*) + . = ALIGN(8); + *(.sdata) } . = %s; . = ALIGN(8); @@ -153,8 +153,6 @@ def tvm_callback_relocate_binary( *(.bss*) . = ALIGN(8); *(.sbss) - . = ALIGN(8); - *(.sdata) } } """ % (text_addr, rodata_addr, data_addr, bss_addr) @@ -191,8 +189,8 @@ def tvm_callback_read_binary_section(binary, section, toolchain_prefix): toolchain_prefix : str prefix for binary names in target compiler toolchain - Return - ------ + Returns + ------- section_bin : bytearray contents of the read section """ @@ -233,8 +231,8 @@ def tvm_callback_get_symbol_map(binary, toolchain_prefix): toolchain_prefix : str prefix for binary names in target compiler toolchain - Return - ------ + Returns + ------- map_str : str map of defined symbols to addresses, encoded as a series of alternating newline-separated keys and values diff --git a/python/tvm/micro/base.py b/python/tvm/micro/base.py index e2f601cb6efe..7cb13c4fa2f5 100644 --- a/python/tvm/micro/base.py +++ b/python/tvm/micro/base.py @@ -21,6 +21,7 @@ import logging import os +import sys from tvm.contrib import util as _util from tvm.contrib import cc as _cc @@ -57,6 +58,7 @@ class Session: def __init__(self, device_type, toolchain_prefix): if device_type not in SUPPORTED_DEVICE_TYPES: raise RuntimeError("unknown micro device type \"{}\"".format(device_type)) + self._check_system() # First, find and compile runtime library. runtime_src_path = os.path.join(_get_micro_device_dir(), "utvm_runtime.c") @@ -69,6 +71,18 @@ def __init__(self, device_type, toolchain_prefix): self._enter = self.module["enter"] self._exit = self.module["exit"] + def _check_system(self): + """Check if the user's system is supported by MicroTVM. + + Raises error if not supported. + """ + if not sys.platform.startswith("linux"): + raise RuntimeError("microTVM is currently only supported on Linux") + # TODO(weberlo): Add 32-bit support. + # It's primarily the compilation pipeline that isn't compatible. + if sys.maxsize <= 2**32: + raise RuntimeError("microTVM is currently only supported on 64-bit platforms") + def __enter__(self): self._enter() @@ -167,9 +181,9 @@ def replace_suffix(s, new_suffix): options = ["-I" + path for path in find_include_path()] options += ["-I{}".format(_get_micro_device_dir())] options += ["-fno-stack-protector"] - # TODO(weberlo): This option cannot be used on 32-bit machines. Make this - # compilation pipeline compatible with 32-bit. - options += ["-mcmodel=large"] + if sys.maxsize > 2**32 and sys.platform.startswith("linux"): + # Only add this option if the host is a 64-bit Linux. + options += ["-mcmodel=large"] compile_cmd = "{}gcc".format(toolchain_prefix) if include_dev_lib_header: diff --git a/src/runtime/micro/micro_session.cc b/src/runtime/micro/micro_session.cc index 4356eb340da9..ca6f4469d406 100644 --- a/src/runtime/micro/micro_session.cc +++ b/src/runtime/micro/micro_session.cc @@ -21,6 +21,16 @@ * Copyright (c) 2019 by Contributors * \file micro_session.cc * \brief session to manage multiple micro modules + * + * Each session consists of an interaction with a *single* logical device. + * Within that interaction, multiple TVM modules can be loaded on the logical + * device. + * + * Multiple sessions can exist simultaneously, but there is only ever one + * *active* session. The idea of an active session mainly has implications for + * the frontend, in that one must make a session active in order to allocate + * new TVM objects on it. Aside from that, previously allocated objects can be + * used even if the session which they belong to is not currently active. */ #include diff --git a/tests/python/contrib/test_binutil.py b/tests/python/contrib/test_binutil.py index 566fb32f534d..617a758d2752 100644 --- a/tests/python/contrib/test_binutil.py +++ b/tests/python/contrib/test_binutil.py @@ -14,6 +14,14 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +"""Test various utilities for interaction with compiled binaries. + +Specifically, we test the following capabilities: + - querying the size of a binary section + - relocating sections within a binary to new addresses + - reading the contents of a binary section + - querying the address of a symbol in the binary +""" import tvm import subprocess @@ -21,6 +29,8 @@ from tvm.contrib import cc from tvm.contrib.binutil import * +TOOLCHAIN_PREFIX = "" + def make_binary(): prog = "int a = 7; \ int main() { \ @@ -29,19 +39,15 @@ def make_binary(): }" tmp_dir = util.tempdir() tmp_source = tmp_dir.relpath("source.c") - tmp_obj = tmp_dir.relpath("obj.o") + tmp_obj = tmp_dir.relpath("obj.obj") with open(tmp_source, "w") as f: f.write(prog) - p1 = subprocess.Popen(["gcc", "-c", tmp_source, "-o", tmp_obj], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - p1.communicate() + cc.create_shared(tmp_obj, tmp_source, [], + compile_cmd="{}gcc".format(TOOLCHAIN_PREFIX)) prog_bin = bytearray(open(tmp_obj, "rb").read()) return prog_bin -TOOLCHAIN_PREFIX = "" - def test_tvm_callback_get_section_size(binary=None): if binary is None: binary = make_binary() diff --git a/tests/python/unittest/test_runtime_micro.py b/tests/python/unittest/test_runtime_micro.py index 8963924dd45e..06461bd978a6 100644 --- a/tests/python/unittest/test_runtime_micro.py +++ b/tests/python/unittest/test_runtime_micro.py @@ -268,6 +268,33 @@ def test_nested_sessions(): add_result, np_tensor_a + 1.0) +def test_inactive_session_use(): + """Test the use of objects allocated in a session that is no longer active.""" + if not tvm.module.enabled("micro_dev"): + return + shape = (1024,) + dtype = "float32" + + # Construct Relay add program. + x = relay.var("x", relay.TensorType(shape=shape, dtype=dtype)) + ret = relay.add(x, relay.const(1.0)) + add_const_func = relay.Function([x], ret) + + sess_a = micro.Session(DEVICE_TYPE, TOOLCHAIN_PREFIX) + sess_b = micro.Session(DEVICE_TYPE, TOOLCHAIN_PREFIX) + with sess_a: + np_tensor_a = np.random.uniform(size=shape).astype(dtype) + micro_tensor_a = tvm.nd.array(np_tensor_a, tvm.micro_dev(0)) + add_const_mod = relay_micro_build(add_const_func, TOOLCHAIN_PREFIX) + + with sess_b: + # These objects belong to `sess_a`. + add_const_mod.run(x=micro_tensor_a) + add_result = add_const_mod.get_output(0).asnumpy() + tvm.testing.assert_allclose( + add_result, np_tensor_a + 1.0) + + if __name__ == "__main__": test_alloc() test_add() @@ -276,3 +303,4 @@ def test_nested_sessions(): test_multiple_modules() test_interleave_sessions() test_nested_sessions() + test_inactive_session_use()