This is an automated email from the ASF dual-hosted git repository.
masahi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 7cfaa88 [Hexagon] Enable running CI tests via simulator (#10473)
7cfaa88 is described below
commit 7cfaa88e6c18edc0a41e1a984d3cb9d8659a1c2c
Author: Krzysztof Parzyszek <[email protected]>
AuthorDate: Sat Mar 5 15:11:12 2022 -0600
[Hexagon] Enable running CI tests via simulator (#10473)
---
Jenkinsfile | 4 ++
python/tvm/contrib/hexagon/hexagon.py | 4 +-
tests/python/contrib/test_hexagon/conftest.py | 15 +++++++
tests/python/contrib/test_hexagon/test_launcher.py | 51 +++++++++++++++++-----
tests/scripts/task_python_hexagon_simulator.sh | 40 +++++++++++++++++
5 files changed, 103 insertions(+), 11 deletions(-)
diff --git a/Jenkinsfile b/Jenkinsfile
index 6550be0..9d38a0e 100755
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -442,6 +442,10 @@ stage('Build') {
script: "${docker_run} ${ci_hexagon}
./tests/scripts/task_python_hexagon.sh",
label: 'Run Hexagon tests',
)
+ sh (
+ script: "${docker_run} ${ci_hexagon}
./tests/scripts/task_python_hexagon_simulator.sh",
+ label: 'Run Hexagon tests on simulator',
+ )
} finally {
junit 'build/pytest-results/*.xml'
}
diff --git a/python/tvm/contrib/hexagon/hexagon.py
b/python/tvm/contrib/hexagon/hexagon.py
index 1e4d85d..9790746 100644
--- a/python/tvm/contrib/hexagon/hexagon.py
+++ b/python/tvm/contrib/hexagon/hexagon.py
@@ -268,6 +268,7 @@ def ir_lower_vtcm_pass():
def create_aot_shared(so_name: Union[str, pathlib.Path], files, hexagon_arch:
str, options=None):
"""Export Hexagon AOT module."""
+ options = options or []
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.'
@@ -286,6 +287,7 @@ def create_aot_shared(so_name: Union[str, pathlib.Path],
files, hexagon_arch: st
tvm_dir = pathlib.Path(os.path.dirname(os.path.realpath(__file__))) / ".."
/ ".." / ".." / ".."
compute_arch = f"compute{hexagon_arch}"
compile_options = [
+ f"-O3",
f"-I{tvm_dir / 'include'}",
f"-I{tvm_dir / '3rdparty' / 'dlpack' / 'include'}",
f"-I{tvm_dir / '3rdparty' / 'dmlc-core' / 'include'}",
@@ -302,4 +304,4 @@ def create_aot_shared(so_name: Union[str, pathlib.Path],
files, hexagon_arch: st
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)
+ cross_compile(str(so_name), c_files, options=compile_options + options)
diff --git a/tests/python/contrib/test_hexagon/conftest.py
b/tests/python/contrib/test_hexagon/conftest.py
index 5c67763..2f2c570 100644
--- a/tests/python/contrib/test_hexagon/conftest.py
+++ b/tests/python/contrib/test_hexagon/conftest.py
@@ -78,3 +78,18 @@ def tvm_tracker_port() -> int:
@tvm.testing.fixture
def adb_server_socket() -> str:
return os.getenv(ADB_SERVER_SOCKET, default="tcp:5037")
+
+
+# If the execution aborts while an RPC server is running, the python
+# code that is supposed to shut it dowm will never execute. This will
+# keep pytest from terminating (indefinitely), so add a cleanup
+# fixture to terminate any still-running servers.
[email protected](scope="session", autouse=True)
+def terminate_rpc_servers():
+ # Since this is a fixture that runs regardless of whether the
+ # execution happens on simulator or on target, make sure the
+ # yield happens every time.
+ serial = os.environ.get(ANDROID_SERIAL_NUMBER)
+ yield []
+ if serial == "simulator":
+ os.system("ps ax | grep tvm_rpc_x86 | awk '{print $1}' | xargs kill")
diff --git a/tests/python/contrib/test_hexagon/test_launcher.py
b/tests/python/contrib/test_hexagon/test_launcher.py
index b7277ce..e9ddc50 100644
--- a/tests/python/contrib/test_hexagon/test_launcher.py
+++ b/tests/python/contrib/test_hexagon/test_launcher.py
@@ -15,6 +15,7 @@
# specific language governing permissions and limitations
# under the License.
+import os
import pathlib
import sys
import pytest
@@ -33,6 +34,12 @@ from .conftest import requires_hexagon_toolchain
RPC_SERVER_PORT = 7070
+# NOTE on server ports:
+# These tests use different port numbers for the RPC server (7070 + ...).
+# The reason is that an RPC session cannot be gracefully closed without
+# triggering TIME_WAIT state on the server socket. This prevents another
+# server to bind to the same port until the wait time elapses.
+
@requires_hexagon_toolchain
def test_add(android_serial_number, tvm_tracker_host, tvm_tracker_port,
adb_server_socket):
@@ -58,7 +65,7 @@ def test_add(android_serial_number, tvm_tracker_host,
tvm_tracker_port, adb_serv
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 0, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -73,9 +80,9 @@ def test_add(android_serial_number, tvm_tracker_host,
tvm_tracker_port, adb_serv
assert (B_data.numpy() == np.array([4])).all()
C_data = tvm.nd.array(np.array([0, 0], dtype=dtype),
device=sess.device)
assert (C_data.numpy() == np.array([0, 0])).all()
-
mod["add"](A_data, B_data, C_data)
assert (C_data.numpy() == np.array([6, 7])).all()
+
launcher.stop_server()
@@ -103,7 +110,7 @@ def test_add_vtcm(android_serial_number, tvm_tracker_host,
tvm_tracker_port, adb
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 1, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -124,6 +131,7 @@ def test_add_vtcm(android_serial_number, tvm_tracker_host,
tvm_tracker_port, adb
mod["add"](A_data, B_data, C_data)
result = C_data.numpy()
assert (result == np.array([6, 7])).all()
+
launcher.stop_server()
@@ -158,7 +166,7 @@ class TestMatMul:
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 2, # See note at the
beginning of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -236,7 +244,7 @@ def test_graph_executor(
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 3, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -248,6 +256,7 @@ def test_graph_executor(
graph_mod.set_input(**params)
graph_mod.run(**inputs)
hexagon_output = graph_mod.get_output(0).numpy()
+
launcher.stop_server()
target_llvm = tvm.target.Target("llvm")
@@ -322,7 +331,7 @@ def test_graph_executor_multiple_conv2d(
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 4, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -347,6 +356,7 @@ def test_graph_executor_multiple_conv2d(
graph_mod.set_input(**params)
graph_mod.run(**inputs)
hexagon_output = graph_mod.get_output(0).numpy()
+
launcher.stop_server()
target_llvm = tvm.target.Target("llvm")
@@ -365,6 +375,17 @@ def test_graph_executor_multiple_conv2d(
tvm.testing.assert_allclose(hexagon_output, expected_output, rtol=1e-4,
atol=1e-5)
+def _workaround_create_aot_shared():
+ # The C codegen uses TVM/RT functions directly. On Hexagon it should use
+ # functions pointers via __TVMxyz variables. This workaround makes the
+ # runtime symbols visible to the compiled shared library.
+ extra_link_flags = os.environ.get("HEXAGON_SHARED_LINK_FLAGS")
+ extra_options = str(extra_link_flags).split() if extra_link_flags else []
+ return lambda so_name, files, hexagon_arch, options:
hexagon.create_aot_shared(
+ so_name, files, hexagon_arch, options=extra_options + options
+ )
+
+
@requires_hexagon_toolchain
def test_aot_executor(tvm_tracker_host, tvm_tracker_port,
android_serial_number, adb_server_socket):
dtype = "float32"
@@ -406,8 +427,12 @@ def test_aot_executor(tvm_tracker_host, tvm_tracker_port,
android_serial_number,
runtime=Runtime("cpp"),
executor=Executor("aot", {"unpacked-api": False, "interface-api":
"c"}),
)
+ # Uncomment this once the workaround is not needed.
+ # lowered.export_library(
+ # dso_binary_path, fcompile=hexagon.create_aot_shared,
hexagon_arch="v68"
+ # )
lowered.export_library(
- dso_binary_path, fcompile=hexagon.create_aot_shared,
hexagon_arch="v68"
+ dso_binary_path, fcompile=_workaround_create_aot_shared(),
hexagon_arch="v68"
)
if not android_serial_number:
@@ -416,7 +441,7 @@ def test_aot_executor(tvm_tracker_host, tvm_tracker_port,
android_serial_number,
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 5, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -428,6 +453,7 @@ def test_aot_executor(tvm_tracker_host, tvm_tracker_port,
android_serial_number,
aot_mod.set_input(**inputs)
aot_mod.run()
hexagon_output = aot_mod.get_output(0).numpy()
+
launcher.stop_server()
target_llvm = tvm.target.Target("llvm")
@@ -506,8 +532,12 @@ def test_aot_executor_multiple_conv2d(
runtime=Runtime("cpp"),
executor=Executor("aot", {"unpacked-api": False, "interface-api":
"c"}),
)
+ # Uncomment this once the workaround is not needed.
+ # lowered.export_library(
+ # dso_binary_path, fcompile=hexagon.create_aot_shared,
hexagon_arch="v68"
+ # )
lowered.export_library(
- dso_binary_path, fcompile=hexagon.create_aot_shared,
hexagon_arch="v68"
+ dso_binary_path, fcompile=_workaround_create_aot_shared(),
hexagon_arch="v68"
)
if not android_serial_number:
@@ -516,7 +546,7 @@ def test_aot_executor_multiple_conv2d(
rpc_info = {
"rpc_tracker_host": tvm_tracker_host,
"rpc_tracker_port": tvm_tracker_port,
- "rpc_server_port": RPC_SERVER_PORT,
+ "rpc_server_port": RPC_SERVER_PORT + 6, # See note at the beginning
of the file
"adb_server_socket": adb_server_socket,
}
launcher = HexagonLauncher(serial_number=android_serial_number,
rpc_info=rpc_info)
@@ -528,6 +558,7 @@ def test_aot_executor_multiple_conv2d(
aot_mod.set_input(**inputs)
aot_mod.run()
hexagon_output = aot_mod.get_output(0).numpy()
+
launcher.stop_server()
target_llvm = tvm.target.Target("llvm")
diff --git a/tests/scripts/task_python_hexagon_simulator.sh
b/tests/scripts/task_python_hexagon_simulator.sh
new file mode 100755
index 0000000..cddd523
--- /dev/null
+++ b/tests/scripts/task_python_hexagon_simulator.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+set -u
+
+source tests/scripts/setup-pytest-env.sh
+
+make cython3
+
+export TVM_TRACKER_PORT=9190
+export TVM_TRACKER_HOST=0.0.0.0
+env PYTHONPATH=python python3 -m tvm.exec.rpc_tracker --host
"${TVM_TRACKER_HOST}" --port "${TVM_TRACKER_PORT}" &
+TRACKER_PID=$!
+sleep 5 # Wait for tracker to bind
+
+# Temporary workaround for symbol visibility
+export HEXAGON_SHARED_LINK_FLAGS="-Lbuild/hexagon_api_output -lhexagon_rpc_sim"
+
+# HEXAGON_TOOLCHAIN is already set
+export HEXAGON_SDK_ROOT=${HEXAGON_SDK_PATH}
+export ANDROID_SERIAL_NUMBER=simulator
+run_pytest ctypes python-contrib-hexagon-simulator
tests/python/contrib/test_hexagon/test_launcher.py
+
+kill ${TRACKER_PID}