Skip to content

Commit 1a2bf88

Browse files
committed
[SME][AOT] Add Fixed Virtual Platform (FVP) functional testing infrastructure
This commit adds the infrastructure required for testing compiled functions that use SME. A more in depth discussion can be found here: https://github.com/apache/tvm-rfcs/blob/main/rfcs/0107-scalable-matrix-extension-enablement.md#testing Specifically, this commit adds: the installation of the AArch64 Architecture Envelope Model (AEM) Fixed Virtual Platform (FVP), supporting files for compiling and running a graph on the FVP, sample tests which can be removed once TVM can generate SME and some enhancements to the AOT testing infrastructure so that TVM compiled functions can be run on the FVP. Change-Id: I60d39fc17b826a9f5c71991d86d3791de83a54d4
1 parent 95ec38b commit 1a2bf88

File tree

6 files changed

+343
-8
lines changed

6 files changed

+343
-8
lines changed

docker/Dockerfile.ci_cpu

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,8 @@ RUN bash /install/ubuntu_install_libxsmm.sh
149149
# ONNX and PyTorch
150150
COPY install/ubuntu_install_onnx.sh /install/ubuntu_install_onnx.sh
151151
RUN bash /install/ubuntu_install_onnx.sh
152+
153+
# AArch64 Architecture Envelope Model (AEM)
154+
COPY install/ubuntu_install_aprofile_aem.sh /install
155+
RUN bash /install/ubuntu_install_aprofile_aem.sh
156+
ENV PATH $PATH:/opt/arm/fvp/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3/:/opt/arm/gcc-aarch64-none-elf/bin
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/bin/bash
2+
# Licensed to the Apache Software Foundation (ASF) under one
3+
# or more contributor license agreements. See the NOTICE file
4+
# distributed with this work for additional information
5+
# regarding copyright ownership. The ASF licenses this file
6+
# to you under the Apache License, Version 2.0 (the
7+
# "License"); you may not use this file except in compliance
8+
# with the License. You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing,
13+
# software distributed under the License is distributed on an
14+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
# KIND, either express or implied. See the License for the
16+
# specific language governing permissions and limitations
17+
# under the License.
18+
19+
# Install the AArch64 Architecture Envelope Model (AEM)
20+
21+
set -e
22+
set -u
23+
set -o pipefail
24+
25+
tmpdir=$(mktemp -d)
26+
27+
cleanup()
28+
{
29+
rm -rf "$tmpdir"
30+
}
31+
32+
trap cleanup 0
33+
34+
pushd "$tmpdir"
35+
36+
# Install GCC toolchain
37+
gcc_install_dir="/opt/arm/gcc-aarch64-none-elf"
38+
gcc_url="https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-elf.tar.xz?rev=28d5199f6db34e5980aae1062e5a6703&hash=D87D4B558F0A2247B255BA15C32A94A9F354E6A8"
39+
gcc_sha="7fe7b8548258f079d6ce9be9144d2a10bd2bf93b551dafbf20fe7f2e44e014b8"
40+
gcc_tar="arm-gnu-toolchain-13.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz"
41+
mkdir -p $gcc_install_dir
42+
curl --retry 64 -sSL $gcc_url -o $gcc_tar
43+
echo "$gcc_sha $gcc_tar" | sha256sum --check
44+
tar -xf $gcc_tar -C $gcc_install_dir --strip-components=1
45+
46+
# Download FVP
47+
fvp_dir="/opt/arm/fvp"
48+
fvp_url="https://developer.arm.com/-/media/Files/downloads/ecosystem-models/FVP_Base_RevC-2xAEMvA_11.24_11_Linux64.tgz"
49+
fvp_sha="0f132334834cbc66889a62dd72057c976d7c7dfcfeec21799e9c78fb2ce24720"
50+
curl --retry 64 -sSL $fvp_url -o fvp.tgz
51+
echo "$fvp_sha fvp.tgz" | sha256sum --check
52+
mkdir -p "$fvp_dir"
53+
tar -xzf fvp.tgz -C "$fvp_dir"
54+
rm -rf doc # Remove some documentation bundled with the package

python/tvm/testing/aot.py

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,15 @@ def _subprocess_check_log_output(cmd, cwd, logfile):
179179
raise RuntimeError(f"Subprocess failed: {cmd}\nstdout:\n{stdout}")
180180

181181

182+
def _get_entrypoint_suffix(target):
183+
# LLVM modules don't use the same entrypoint suffix
184+
# as C source generated modules.
185+
if target.kind.name == "llvm":
186+
return "__tvm_main__"
187+
else:
188+
return "run"
189+
190+
182191
def _mangle_name(mod_name, name):
183192
mod_name = mangle_module_name(mod_name)
184193
return mod_name + "_" + name
@@ -385,7 +394,14 @@ def _emit_main_fake_packed_values(main_file):
385394
)
386395

387396

388-
def _emit_main_packed_call(main_file, input_map, output_list, mod_name):
397+
def _emit_entry_function_forward_declaration(main_file, mod_name, entrypoint_suffix):
398+
main_file.write(
399+
f"int {_mangle_name(mod_name, entrypoint_suffix)}"
400+
f"(TVMValue[], int32_t[], int32_t, void*, int32_t, void*);\n"
401+
)
402+
403+
404+
def _emit_main_packed_call(main_file, input_map, output_list, mod_name, entrypoint_suffix):
389405
tensors_name = _mangle_name(mod_name, "tensors")
390406
values_name = _mangle_name(mod_name, "values")
391407
typeids_name = _mangle_name(mod_name, "typeids")
@@ -420,7 +436,8 @@ def fake_tensor(source, source_index, packed_index):
420436
fake_tensor(_mangle_name(mod_name, "outputs"), i, i + num_inputs)
421437

422438
main_file.write(
423-
f'{_mangle_name(mod_name, "run")}({values_name}, {typeids_name}, 0, NULL, 0, NULL);\n'
439+
f"{_mangle_name(mod_name, entrypoint_suffix)}"
440+
f"({values_name}, {typeids_name}, 0, NULL, 0, NULL);\n"
424441
)
425442
main_file.write("\n")
426443

@@ -544,6 +561,15 @@ def _create_main(
544561
model = compiled_model.model
545562
_emit_main_data(main_file, model.inputs, model.outputs, model.name)
546563

564+
if interface_api == "packed":
565+
for compiled_model in compiled_models:
566+
entrypoint_suffix = _get_entrypoint_suffix(
567+
compiled_model.executor_factory.target[0]
568+
)
569+
_emit_entry_function_forward_declaration(
570+
main_file, compiled_model.model.name, entrypoint_suffix
571+
)
572+
547573
_emit_main_prologue(
548574
main_file,
549575
custom_prologue,
@@ -592,7 +618,12 @@ def _create_main(
592618
for compiled_model in compiled_models:
593619
model = compiled_model.model
594620
_emit_main_data_setup(main_file, model.inputs, model.outputs, model.name)
595-
_emit_main_packed_call(main_file, model.inputs, model.outputs, model.name)
621+
entrypoint_suffix = _get_entrypoint_suffix(
622+
compiled_model.executor_factory.target[0]
623+
)
624+
_emit_main_packed_call(
625+
main_file, model.inputs, model.outputs, model.name, entrypoint_suffix
626+
)
596627

597628
for compiled_model in compiled_models:
598629
model = compiled_model.model
@@ -665,14 +696,18 @@ def compile_models(
665696
workspace_memory_pools=None,
666697
constant_memory_pools=None,
667698
schedule_name: str = None,
699+
runtime: tvm.relay.backend.Runtime = Runtime("crt"),
668700
) -> List[AOTCompiledTestModel]:
669701
"""
670702
This method generates runtime.Modules for the tests
671703
"""
672704
if not isinstance(models, list):
673705
models = [models]
674706

675-
runtime = Runtime("crt")
707+
assert (
708+
runtime.name == "crt"
709+
), f"Currently only 'crt' is supported by the test framework, but got {runtime.name}"
710+
676711
executor = Executor(
677712
"aot",
678713
{
@@ -835,10 +870,12 @@ def run_and_check_body(base_path):
835870
makefile_dir = os.path.join(file_dir, "../../../tests/python/relay/aot")
836871
codegen_path = os.path.join(base_path, "codegen")
837872
makefile = os.path.join(makefile_dir, f"{runner.makefile}.mk")
838-
fvp_dir = "/opt/arm/FVP_Corstone_SSE-300/models/Linux64_GCC-6.4/"
839-
# TODO(@grant-arm): Remove once ci_cpu docker image has been updated to FVP_Corstone_SSE
840-
if not os.path.isdir(fvp_dir):
841-
fvp_dir = "/opt/arm/FVP_Corstone_SSE-300_Ethos-U55/models/Linux64_GCC-6.4/"
873+
874+
if runner.makefile == "aprofile_aem":
875+
fvp_dir = "/opt/arm/fvp/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3/"
876+
else:
877+
fvp_dir = "/opt/arm/FVP_Corstone_SSE-300/models/Linux64_GCC-6.4/"
878+
842879
custom_params = " ".join(
843880
[f" {param}='{value}'" for param, value in runner.parameters.items()]
844881
)
@@ -901,11 +938,28 @@ def compile_and_run(
901938
debug_last_error: bool = False,
902939
checker: Optional[Callable[[str], bool]] = None,
903940
print_output_on_mismatch: bool = False,
941+
runtime: tvm.relay.backend.Runtime = Runtime("crt"),
904942
) -> bool:
905943
"""This is a wrapper API to compile and run models as test for AoT
906944
907945
Parameters
908946
----------
947+
interface_api : str
948+
The external calling convention interface API.
949+
950+
Examples: "c", "packed"
951+
952+
use_unpacked_api : bool
953+
Whether or not to use type-erased API internally for the
954+
operator calling convention.
955+
956+
Note: This feature can be useful for embedded targets
957+
when space is at a premium.
958+
959+
Permitted values when interface API is:
960+
> "c": True
961+
> "packed": True/False
962+
909963
test_dir : str
910964
This path will contain build, codegen, include directories.
911965
@@ -935,6 +989,7 @@ def compile_and_run(
935989
use_runtime_executor=use_runtime_executor,
936990
target=target,
937991
schedule_name=schedule_name,
992+
runtime=runtime,
938993
)
939994

940995
return run_and_check(

tests/python/integration/test_arm_aprofile.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616
# under the License.
1717
"""Tests for Arm(R) A-Profile Architecture."""
1818
import os
19+
import subprocess
20+
1921
import numpy as np
2022
import pytest
23+
2124
import tvm
2225
import tvm.testing
2326
from tvm import relay
2427
from tvm.relay.transform import ToMixedPrecision, FoldConstant
2528
from tvm.relay.build_module import bind_params_by_name
29+
from tvm.testing.aot import AOTTestModel, AOTTestRunner, generate_ref_data, compile_and_run
30+
from tvm.contrib import utils
2631

2732

2833
def get_mattr(dtype):
@@ -73,3 +78,96 @@ def test_conv2d(dtype):
7378
with tvm.transform.PassContext(opt_level=3):
7479
lib = tvm.relay.build(mod, target=target, params=params)
7580
lib.export_library(lib_path, cc="aarch64-linux-gnu-gcc")
81+
82+
83+
# AOT Test Runner using the AArch64 Architecture Envelope Model (AEM)
84+
# Fixed Virtual Platform (FVP) reference system.
85+
# See: https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms
86+
AOT_APROFILE_AEM_RUNNER = AOTTestRunner(
87+
makefile="aprofile_aem",
88+
pass_config={
89+
"tir.usmp.enable": False,
90+
"tir.disable_assert": True, # AOT test infra creates 'fake' inputs that fail asserts
91+
},
92+
)
93+
94+
95+
@tvm.testing.requires_x86
96+
def test_aem_simple_addition():
97+
"""Tests a simple addition running on the AArch64 AEM."""
98+
inp = relay.var("data", shape=(1, 2, 4, 4))
99+
add = relay.add(inp, relay.const(np.ones((1, 2, 4, 4))))
100+
func = relay.Function([inp], add)
101+
ir_mod = tvm.IRModule.from_expr(func)
102+
ir_mod = tvm.relay.transform.InferType()(ir_mod)
103+
104+
main_func = ir_mod["main"]
105+
shape_dict = {p.name_hint: p.checked_type.concrete_shape for p in main_func.params}
106+
type_dict = {p.name_hint: p.checked_type.dtype for p in main_func.params}
107+
108+
input_data = np.random.uniform(size=shape_dict["data"]).astype(type_dict["data"])
109+
params = {}
110+
inputs = {"data": input_data}
111+
ref_outputs = generate_ref_data(ir_mod, inputs, params)
112+
113+
compile_and_run(
114+
AOTTestModel(module=ir_mod, inputs=inputs, outputs=ref_outputs, params=params),
115+
target=tvm.target.Target("llvm -mtriple=aarch64-none-elf"),
116+
runtime=tvm.relay.backend.Runtime("crt", {"system-lib": True}),
117+
interface_api="packed",
118+
use_unpacked_api=False,
119+
runner=AOT_APROFILE_AEM_RUNNER,
120+
)
121+
122+
123+
@tvm.testing.requires_x86
124+
def test_aem_asm_sme():
125+
"""
126+
Tests SME assembly runs on the AArch64 AEM. This test is used as a simple
127+
sanity check until the TVM schedules are able to produce SME.
128+
"""
129+
c_code = """
130+
#include <stdio.h>
131+
132+
int main(void) {
133+
__asm volatile(
134+
"smstart\\n"
135+
"smstop\\n"
136+
);
137+
printf("EXITTHESIM\\n");
138+
return 0;
139+
}
140+
"""
141+
runner = AOT_APROFILE_AEM_RUNNER
142+
143+
tmpdir = utils.tempdir()
144+
build_path = os.path.join(tmpdir.path, "build")
145+
os.makedirs(build_path, exist_ok=True)
146+
147+
with open(build_path + "/test.c", "w") as f:
148+
f.write(c_code)
149+
150+
file_dir = os.path.dirname(os.path.abspath(__file__))
151+
makefile_dir = os.path.join(file_dir, "../../../tests/python/relay/aot")
152+
makefile = os.path.join(makefile_dir, f"{runner.makefile}.mk")
153+
154+
make_command = (
155+
f"make -f {makefile} build_dir={build_path}"
156+
+ f" TVM_ROOT={file_dir}/../../.."
157+
+ f" AOT_TEST_ROOT={makefile_dir}"
158+
+ " FVP_DIR=/opt/arm/fvp/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3/"
159+
)
160+
161+
compile_command = f"{make_command} aot_test_runner"
162+
popen = subprocess.Popen(compile_command, cwd=build_path, shell=True, stdout=subprocess.PIPE)
163+
return_code = popen.wait()
164+
assert not return_code, "Failed to compile"
165+
166+
run_command = f"{make_command} run"
167+
popen = subprocess.Popen(run_command, cwd=build_path, shell=True, stdout=subprocess.PIPE)
168+
return_code = popen.wait()
169+
assert not return_code, "Failed to run"
170+
171+
172+
if __name__ == "__main__":
173+
tvm.testing.main()

0 commit comments

Comments
 (0)