Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[runtime][Hexagon] AOTExecutor implementation for C Codegen #10311

Merged
merged 2 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion python/tvm/contrib/hexagon/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,25 @@ def get_graph_executor(
graph_mod = self.load_module(module_name, session)
return tvm.contrib.graph_executor.create(graph_json, graph_mod, session.device)

def get_aot_executor(self, module_name: Union[str, pathlib.Path], session: Session):
"""Create a local AoTModule which consumes a remote libmod.

Parameters
----------
module_name : str or pathlib.Path
Remote module filename. Same restrictions apply as in load_module().
session : Session
Remote session. The session must be established (via __enter__)
prior to calling this function.

Returns
-------
aot_module : AotModule
Runtime AOT module that can be used to execute.
"""
aot_mod = self.load_module(module_name, session)
return tvm.runtime.executor.AotModule(aot_mod["default"](session.device))


class HexagonLauncherAndroid(HexagonLauncherRPC):
"""Hexagon Launcher for Android."""
Expand Down Expand Up @@ -284,7 +303,7 @@ def _copy_to_remote(

def _create_remote_directory(self, remote_path: Union[str, pathlib.Path]):
"""Abstract method implementation. See description in HexagonLauncherRPC."""
subprocess.check_call(self._adb_device_sub_cmd + ["shell", "mkdir", "-p", str(path)])
subprocess.check_call(self._adb_device_sub_cmd + ["shell", "mkdir", "-p", str(remote_path)])

def _copy_binaries(self):
"""Upload Android server binaries."""
Expand Down
71 changes: 63 additions & 8 deletions python/tvm/contrib/hexagon/hexagon.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

import functools as ft
import os
import pathlib
from typing import Union

import tvm
import tvm.ir
import tvm.contrib.cc as cc
Expand All @@ -39,10 +42,18 @@
#
# Subsequent calls to 'link_shared' will use the newly registered linker.

hexagon_toolchain_root = os.environ.get("HEXAGON_TOOLCHAIN") or "" # pylint: disable=invalid-name
hexagon_link_main = os.path.join( # pylint: disable=invalid-name
hexagon_toolchain_root, "bin", "hexagon-link"
)
HEXAGON_TOOLCHAIN = os.environ.get("HEXAGON_TOOLCHAIN", default="") # pylint: disable=invalid-name
HEXAGON_SDK_PATH = os.environ.get("HEXAGON_SDK_PATH", default="") # pylint: disable=invalid-name
HEXAGON_LINK_MAIN = (
pathlib.Path(HEXAGON_TOOLCHAIN) / "bin" / "hexagon-link"
) # pylint: disable=invalid-name
HEXAGON_CLANG_PLUS = (
pathlib.Path(HEXAGON_TOOLCHAIN) / "bin" / "hexagon-clang++"
) # pylint: disable=invalid-name
HEXAGON_SDK_INCLUDE_DIRS = [ # pylint: disable=invalid-name
pathlib.Path(HEXAGON_SDK_PATH) / "incs",
pathlib.Path(HEXAGON_SDK_PATH) / "incs" / "stddef",
]


def register_linker(f):
Expand All @@ -51,9 +62,14 @@ def register_linker(f):


@register_func("tvm.contrib.hexagon.hexagon.hexagon_link")
def hexagon_link():
def hexagon_link() -> str:
"""Return path to the Hexagon linker."""
return hexagon_link_main
return str(HEXAGON_LINK_MAIN)


def hexagon_clang_plus() -> str:
"""Return path to the Hexagon clang++."""
return str(HEXAGON_CLANG_PLUS)


@register_func("tvm.contrib.hexagon.hexagon.link_shared")
Expand Down Expand Up @@ -101,12 +117,12 @@ def to_str(s):
message += (
" Please verify the value of the HEXAGON_LINKER environment variable "
+ '(currently set to "'
+ hexagon_toolchain_root
+ HEXAGON_TOOLCHAIN
+ '").'
)
raise Exception(message)

libpath = os.path.join(hexagon_toolchain_root, "target", "hexagon", "lib", "v66", "G0")
libpath = os.path.join(HEXAGON_TOOLCHAIN, "target", "hexagon", "lib", "v66", "G0")
cc.create_shared(
so_name,
objs,
Expand Down Expand Up @@ -248,3 +264,42 @@ def transform(func, mod, ctx):

def ir_lower_vtcm_pass():
return [(3, ir_lower_vtcm())]


def create_aot_shared(so_name: Union[str, pathlib.Path], files, hexagon_arch: str, options=None):
"""Export Hexagon AOT module."""
if not os.access(str(HEXAGON_CLANG_PLUS), os.X_OK):
raise Exception(
'The Clang++ "' + str(HEXAGON_CLANG_PLUS) + '" does not exist or is not executable.'
)
if not HEXAGON_TOOLCHAIN:
raise Exception(
" The environment variable HEXAGON_TOOLCHAIN is unset. Please export "
+ "HEXAGON_TOOLCHAIN in your environment."
)
if not HEXAGON_SDK_PATH:
raise Exception(
" The environment variable HEXAGON_SDK_PATH is unset. Please export "
+ "HEXAGON_SDK_PATH in your environment."
)

tvm_dir = pathlib.Path(os.path.dirname(os.path.realpath(__file__))) / ".." / ".." / ".." / ".."
compute_arch = f"compute{hexagon_arch}"
compile_options = [
f"-I{tvm_dir / 'include'}",
f"-I{tvm_dir / '3rdparty' / 'dlpack' / 'include'}",
f"-I{tvm_dir / '3rdparty' / 'dmlc-core' / 'include'}",
f"-I{pathlib.Path(HEXAGON_SDK_PATH) / 'rtos' / 'qurt' / compute_arch / 'include'/ 'posix'}",
f"-I{pathlib.Path(HEXAGON_SDK_PATH) / 'rtos' / 'qurt' / compute_arch / 'include' / 'qurt'}",
f"-DDMLC_USE_LOGGING_LIBRARY=<tvm/runtime/logging.h>",
f"-D_MACH_I32=int",
]

# For debugging
for path in HEXAGON_SDK_INCLUDE_DIRS:
compile_options.append(f"-I{str(path)}")

cross_compile = cc.cross_compiler(compile_func=hexagon_clang_plus())
cross_compile.output_format = "o"
c_files = [str(file) for file in files]
cross_compile(str(so_name), c_files, options=compile_options)
1 change: 1 addition & 0 deletions python/tvm/contrib/hexagon/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from typing import Union
from tvm import rpc as _rpc


class Session:
"""Hexagon Device Session

Expand Down
3 changes: 3 additions & 0 deletions src/relay/backend/aot_executor_codegen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,9 @@ class AOTExecutorCodegenModule : public runtime::ModuleNode {
if (!target_host.defined() && it.second->kind->device_type == kDLCPU) {
target_host = it.second;
}
if (!target_host.defined() && it.second->kind->device_type == kDLHexagon) {
target_host = *(new Target("c"));
}
ICHECK(dev_type);
targets[static_cast<DLDeviceType>(dev_type->value)] = it.second;
}
Expand Down
7 changes: 5 additions & 2 deletions src/runtime/aot_executor/aot_executor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ AotExecutor::AotExecutor(tvm::runtime::Module module, const std::vector<Device>&
DLDevice expected_device{kDLCPU, 0};
ICHECK_EQ(devices_[0].device_id, expected_device.device_id)
<< "At this time, AOTExecutor supports only execution on kDLCPU 0";
ICHECK_EQ(devices_[0].device_type, expected_device.device_type)
<< "At this time, AOTExecutor supports only execution on kDLCPU 0";
// TODO(tvm-team): Temporary hack since Hexagon is defined different than kDLCPU.
bool is_valid_device = (TVMDeviceExtType(devices_[0].device_type) == kDLHexagon) ||
(DLDeviceType(devices_[0].device_type) == kDLCPU);
CHECK(is_valid_device)
<< "At this time, AOTExecutor supports only execution on kDLCPU 0 or kDLHexagon 0";

for (auto input : metadata_->inputs()) {
// TODO(areusch): Encode device information in Metadata.
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/crt/microtvm_rpc_server/rpc_server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
// NOTE: dmlc/base.h contains some declarations that are incompatible with some C embedded
// toolchains. Just pull the bits we need for this file.
#define DMLC_CMAKE_LITTLE_ENDIAN DMLC_IO_USE_LITTLE_ENDIAN
#define DMLC_LITTLE_ENDIAN true
#define DMLC_LITTLE_ENDIAN 1
#include <tvm/runtime/c_runtime_api.h>
#include <tvm/runtime/crt/crt.h>
#include <tvm/runtime/crt/logging.h>
Expand Down
10 changes: 6 additions & 4 deletions src/runtime/hexagon/hexagon/hexagon_device_api_v2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void HexagonDeviceAPIv2::GetAttr(Device dev, DeviceAttrKind kind, TVMRetValue* r
// DataSpace: static allocations for Hexagon
void* HexagonDeviceAPIv2::AllocDataSpace(Device dev, int ndim, const int64_t* shape,
DLDataType dtype, Optional<String> mem_scope) {
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon);
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon) << "dev.device_type: " << dev.device_type;

// Forcing contiguous allocation, for now
// TODO(Straw): Enable discontiguous allocation after RFC 39 lands
Expand Down Expand Up @@ -84,7 +84,9 @@ void* HexagonDeviceAPIv2::AllocDataSpace(Device dev, size_t nbytes, size_t align
}

void HexagonDeviceAPIv2::FreeDataSpace(Device dev, void* ptr) {
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon);
bool is_valid_device = (TVMDeviceExtType(dev.device_type) == kDLHexagon) ||
(DLDeviceType(dev.device_type) == kDLCPU);
CHECK(is_valid_device) << "dev.device_type: " << dev.device_type;
auto* hexbuf = static_cast<HexagonBuffer*>(ptr);
CHECK(hexbuf != nullptr);
delete hexbuf;
Expand All @@ -97,7 +99,7 @@ struct HexagonWorkspacePool : public WorkspacePool {
};

void* HexagonDeviceAPIv2::AllocWorkspace(Device dev, size_t size, DLDataType type_hint) {
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon);
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon) << "dev.device_type: " << dev.device_type;
auto* hexbuf = static_cast<HexagonBuffer*>(
dmlc::ThreadLocalStore<HexagonWorkspacePool>::Get()->AllocWorkspace(dev, size));

Expand All @@ -109,7 +111,7 @@ void* HexagonDeviceAPIv2::AllocWorkspace(Device dev, size_t size, DLDataType typ
}

void HexagonDeviceAPIv2::FreeWorkspace(Device dev, void* data) {
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon);
CHECK(TVMDeviceExtType(dev.device_type) == kDLHexagon) << "dev.device_type: " << dev.device_type;
auto it = workspace_allocations_.find(data);
CHECK(it != workspace_allocations_.end())
<< "Attempt made to free unknown or already freed workspace allocation";
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/minrpc/minrpc_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
#ifndef TVM_RUNTIME_MINRPC_MINRPC_SERVER_H_
#define TVM_RUNTIME_MINRPC_MINRPC_SERVER_H_

#define DMLC_LITTLE_ENDIAN true
#define DMLC_LITTLE_ENDIAN 1
#include <string.h>
#include <tvm/runtime/c_runtime_api.h>

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/vm/vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ void VirtualMachine::SetOneInput(std::string func_name, const TVMArgValue& tag,
const auto& vm_func = CheckAndGetVMFunction(func_name);
size_t params_num = vm_func.params.size();

int inp_index;
int inp_index = 0;
if (tag.type_code() == kTVMArgInt) {
inp_index = tag;
} else if (tag.type_code() == kTVMStr) {
Expand Down
6 changes: 3 additions & 3 deletions tests/python/contrib/test_hexagon/test_cache_read_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,9 @@ def test_cache_read_write(android_serial_number, tvm_tracker_host, tvm_tracker_p
pytest.skip("Skip hardware test since ANDROID_SERIAL_NUMBER is not set.")

rpc_info = {
"rpc_tracker_host" : tvm_tracker_host,
"rpc_tracker_port" : tvm_tracker_port,
"rpc_server_port" : 7070,
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
"rpc_server_port": 7070,
}
launcher = HexagonLauncher(serial_number=android_serial_number, rpc_info=rpc_info)
launcher.upload(dso_binary_path, dso_binary)
Expand Down
Loading