Skip to content

Commit

Permalink
[TVMC] Add codegen args to tvmc (apache#10190)
Browse files Browse the repository at this point in the history
* [TVMC] Add codegen args to tvmc

This enables external codegen arguments similar to those for `Target`s:

```
tvmc compile --target=cmsis-nn,c --target-cmsis-nn-mcpu=cortex-m55
```

* Add CMSIS-NN decorator to dependent tests
  • Loading branch information
Mousius authored and ylc committed Feb 16, 2022
1 parent 7648a86 commit 44d77df
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 2 deletions.
4 changes: 3 additions & 1 deletion apps/microtvm/ethosu/run_demo.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ curl --retry 64 -sSL ${mobilenet_url} -o ./mobilenet_v2_1.0_224_INT8.tflite
# Compile model for Arm(R) Cortex(R)-M55 CPU and Ethos(TM)-U55 NPU
# An alternative to using "python3 -m tvm.driver.tvmc" is to call
# "tvmc" directly once TVM has been pip installed.
python3 -m tvm.driver.tvmc compile --target="ethos-u -accelerator_config=ethos-u55-256, cmsis-nn, c" \
python3 -m tvm.driver.tvmc compile --target=ethos-u,cmsis-nn,c \
--target-ethos-u-accelerator_config=ethos-u55-256 \
--target-cmsis-nn-mcpu=cortex-m55 \
--target-c-mcpu=cortex-m55 \
--runtime=crt \
--executor=aot \
Expand Down
57 changes: 56 additions & 1 deletion python/tvm/driver/tvmc/target.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
import tvm
from tvm.driver import tvmc
from tvm.driver.tvmc import TVMCException
from tvm.driver.tvmc.composite_target import get_codegen_by_target, get_codegen_names
from tvm.ir.attrs import make_node, _ffi_api as attrs_api
from tvm.ir.transform import PassContext
from tvm.target import Target, TargetKind

# pylint: disable=invalid-name
Expand Down Expand Up @@ -54,6 +57,25 @@ def _generate_target_kind_args(parser, kind_name):
)


def _generate_codegen_args(parser, codegen_name):
codegen = get_codegen_by_target(codegen_name)
pass_configs = PassContext.list_configs()

if codegen["config_key"] is not None and codegen["config_key"] in pass_configs:
target_group = parser.add_argument_group(f"target {codegen_name}")
attrs = make_node(pass_configs[codegen["config_key"]]["type"])
fields = attrs_api.AttrsListFieldInfo(attrs)
for field in fields:
for tvm_type, python_type in INTERNAL_TO_NATIVE_TYPE.items():
if field.type_info.startswith(tvm_type):
target_option = field.name
target_group.add_argument(
f"--target-{codegen_name}-{target_option}",
type=python_type,
help=f"target {codegen_name} {target_option}{python_type}",
)


def generate_target_args(parser):
"""Walks through the TargetKind registry and generates arguments for each Target's options"""
parser.add_argument(
Expand All @@ -63,6 +85,8 @@ def generate_target_args(parser):
)
for target_kind in _valid_target_kinds():
_generate_target_kind_args(parser, target_kind)
for codegen_name in get_codegen_names():
_generate_codegen_args(parser, codegen_name)


def _reconstruct_target_kind_args(args, kind_name):
Expand All @@ -76,13 +100,40 @@ def _reconstruct_target_kind_args(args, kind_name):
return kind_options


def _reconstruct_codegen_args(args, codegen_name):
codegen = get_codegen_by_target(codegen_name)
pass_configs = PassContext.list_configs()
codegen_options = {}

if codegen["config_key"] is not None and codegen["config_key"] in pass_configs:
attrs = make_node(pass_configs[codegen["config_key"]]["type"])
fields = attrs_api.AttrsListFieldInfo(attrs)
for field in fields:
for tvm_type in INTERNAL_TO_NATIVE_TYPE:
if field.type_info.startswith(tvm_type):
target_option = field.name
var_name = (
f"target_{codegen_name.replace('-', '_')}_{target_option.replace('-', '_')}"
)
option_value = getattr(args, var_name)
if option_value is not None:
codegen_options[target_option] = option_value
return codegen_options


def reconstruct_target_args(args):
"""Reconstructs the target options from the arguments"""
reconstructed = {}
for target_kind in _valid_target_kinds():
kind_options = _reconstruct_target_kind_args(args, target_kind)
if kind_options:
reconstructed[target_kind] = kind_options

for codegen_name in get_codegen_names():
codegen_options = _reconstruct_codegen_args(args, codegen_name)
if codegen_options:
reconstructed[codegen_name] = codegen_options

return reconstructed


Expand Down Expand Up @@ -349,6 +400,10 @@ def target_from_cli(target, additional_target_options=None):
target = _recombobulate_target(tvm_targets[0])
target_host = _recombobulate_target(tvm_targets[1])

extra_targets = [t for t in parsed_targets if not t["is_tvm_target"]]
extra_targets = [
_combine_target_options(t, additional_target_options)
for t in parsed_targets
if not t["is_tvm_target"]
]

return tvm.target.Target(target, host=target_host), extra_targets
44 changes: 44 additions & 0 deletions tests/python/driver/tvmc/test_target_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import pytest

import tvm
from tvm.driver.tvmc import TVMCException
from tvm.driver.tvmc.target import generate_target_args, reconstruct_target_args, target_from_cli

Expand All @@ -34,13 +35,44 @@ def test_target_to_argparse():
assert parsed.target_llvm_mattr == "+fp,+mve"


@tvm.testing.requires_cmsisnn
def test_target_to_argparse_known_codegen():
parser = argparse.ArgumentParser()
generate_target_args(parser)
parsed, _ = parser.parse_known_args(
[
"--target=cmsis-nn,llvm",
"--target-cmsis-nn-mcpu=cortex-m3",
"--target-llvm-mattr=+fp,+mve",
"--target-llvm-mcpu=cortex-m3",
]
)
assert parsed.target == "cmsis-nn,llvm"
assert parsed.target_llvm_mcpu == "cortex-m3"
assert parsed.target_llvm_mattr == "+fp,+mve"
assert parsed.target_cmsis_nn_mcpu == "cortex-m3"


def test_mapping_target_args():
parser = argparse.ArgumentParser()
generate_target_args(parser)
parsed, _ = parser.parse_known_args(["--target=llvm", "--target-llvm-mcpu=cortex-m3"])
assert reconstruct_target_args(parsed) == {"llvm": {"mcpu": "cortex-m3"}}


@tvm.testing.requires_cmsisnn
def test_include_known_codegen():
parser = argparse.ArgumentParser()
generate_target_args(parser)
parsed, _ = parser.parse_known_args(
["--target=cmsis-nn,c", "--target-cmsis-nn-mcpu=cortex-m55", "--target-c-mcpu=cortex-m55"]
)
assert reconstruct_target_args(parsed) == {
"c": {"mcpu": "cortex-m55"},
"cmsis-nn": {"mcpu": "cortex-m55"},
}


def test_skip_target_from_codegen():
parser = argparse.ArgumentParser()
generate_target_args(parser)
Expand Down Expand Up @@ -69,6 +101,18 @@ def test_target_recombobulation_many():
assert "-mcpu=cortex-m3" in str(tvm_target.host)


def test_target_recombobulation_codegen():
tvm_target, extras = target_from_cli(
"cmsis-nn, c -mcpu=cortex-m55",
{"cmsis-nn": {"mcpu": "cortex-m55"}},
)

assert "-mcpu=cortex-m55" in str(tvm_target)
assert len(extras) == 1
assert extras[0]["name"] == "cmsis-nn"
assert extras[0]["opts"] == {"mcpu": "cortex-m55"}


def test_error_if_target_missing():
with pytest.raises(
TVMCException,
Expand Down

0 comments on commit 44d77df

Please sign in to comment.