forked from pytorch/executorch
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvgf_backend.py
More file actions
126 lines (105 loc) · 4.44 KB
/
vgf_backend.py
File metadata and controls
126 lines (105 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Copyright 2025 Arm Limited and/or its affiliates.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
#
# Main implementation of AoT flow to partition and preprocess for VGF target
# backends. This flow converts via TOSA, to an encoding of TOSA known as VGF
# this form is used where the final JIT compile is performed on target (in the
# runtime delegate executorch::runtime::BackendInterface::init
#
import logging
import os
import subprocess
import tempfile
from typing import final, List
from executorch.backends.arm.tosa_backend import (
arm_get_first_delegation_tag,
TOSABackend,
)
from executorch.exir.backend.backend_details import BackendDetails, PreprocessResult
from executorch.exir.backend.compile_spec_schema import CompileSpec
from torch.export.exported_program import ExportedProgram
# debug functionality
logger = logging.getLogger(__name__)
@final
class VgfBackend(BackendDetails):
"""
BackendDetails subclass for delegation to VGF compatible devices. This enables
encapsulated TOSA on target device and JIT compilation on suitable platforms.
"""
@staticmethod
def _compile_tosa_flatbuffer(
tosa_flatbuffer: bytes,
compile_spec: List[CompileSpec],
tag_name: str = "",
) -> bytes:
"""
Static helper method to do the compilation of the TOSA flatbuffer
representation to a target specific binary stream.
"""
compile_flags = []
artifact_path = None
for spec in compile_spec:
if spec.key == "compile_flags":
compile_flags.append(spec.value.decode())
if spec.key == "debug_artifact_path":
artifact_path = spec.value.decode()
# Pass on the TOSA flatbuffer to the vgf compiler.
binary = vgf_compile(tosa_flatbuffer, compile_flags, artifact_path, tag_name)
return binary
@staticmethod
def preprocess(
edge_program: ExportedProgram,
compile_spec: List[CompileSpec],
) -> PreprocessResult:
logger.info(f"{VgfBackend.__name__} preprocess")
# deduce TOSA compile_spec from VGF compile spec. We get a new
# compile spec list, containing only elements relevant for the
# TOSABackend.
tosa_compile_spec = TOSABackend.filter_tosa_compile_specs(compile_spec)
# Backends doesn't allow inheritance, as stated in comments in exir/backend/backend_api.py
# ('All backend implementation are final...'), so use composition instead.
# preprocess returns the serialized TOSA flatbuffer in .processed_bytes,
# which can be passed on to next compilation step.
tosa_preprocess = TOSABackend.preprocess(edge_program, tosa_compile_spec)
tag_name = arm_get_first_delegation_tag(edge_program.graph_module)
binary = VgfBackend._compile_tosa_flatbuffer(
tosa_preprocess.processed_bytes, compile_spec, tag_name
)
return PreprocessResult(processed_bytes=binary)
def vgf_compile(
tosa_flatbuffer: bytes,
compile_flags: List[str],
artifact_path: str | None = None,
tag_name: str = "",
):
with tempfile.TemporaryDirectory() as tmpdir:
# We currently write out a flatbuffer as input to the converter
tosaname = f"output_{tag_name}.tosa"
tosa_path = os.path.join(tmpdir, tosaname)
with open(tosa_path, "wb") as f:
f.write(tosa_flatbuffer)
additional_flags = " ".join(compile_flags)
vgf_path = tosa_path + ".vgf"
conversion_command = (
f"converter-backend {additional_flags} -i {tosa_path} -o {vgf_path}"
)
try:
subprocess.run(
[conversion_command], shell=True, check=True, capture_output=True
)
except subprocess.CalledProcessError as process_error:
raise RuntimeError(
f"Vgf compiler ('{conversion_command}') failed with error:\n \
{process_error.stderr.decode()}\n \
Stdout:\n{process_error.stdout.decode()}"
)
if artifact_path is not None:
logger.info(f"Emitting debug output to: {vgf_path=}")
os.makedirs(artifact_path, exist_ok=True)
cp = f"cp {vgf_path} {artifact_path}"
subprocess.run(cp, shell=True, check=True, capture_output=False)
vgf_bytes = open(vgf_path, "rb").read()
return vgf_bytes