xref: /aosp_15_r20/external/executorch/backends/arm/arm_backend.py (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1*523fa7a6SAndroid Build Coastguard Worker# Copyright 2023-2024 Arm Limited and/or its affiliates.
2*523fa7a6SAndroid Build Coastguard Worker#
3*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the
4*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree.
5*523fa7a6SAndroid Build Coastguard Worker
6*523fa7a6SAndroid Build Coastguard Worker# pyre-unsafe
7*523fa7a6SAndroid Build Coastguard Worker
8*523fa7a6SAndroid Build Coastguard Worker#
9*523fa7a6SAndroid Build Coastguard Worker# Main implementation of AoT flow to partition and preprocess for Arm target
10*523fa7a6SAndroid Build Coastguard Worker# backends. Converts via TOSA as an intermediate form supported by AoT and
11*523fa7a6SAndroid Build Coastguard Worker# JIT compiler flows.
12*523fa7a6SAndroid Build Coastguard Worker#
13*523fa7a6SAndroid Build Coastguard Worker
14*523fa7a6SAndroid Build Coastguard Workerimport logging
15*523fa7a6SAndroid Build Coastguard Workerimport os
16*523fa7a6SAndroid Build Coastguard Workerfrom typing import final, List, Optional
17*523fa7a6SAndroid Build Coastguard Worker
18*523fa7a6SAndroid Build Coastguard Workerimport serializer.tosa_serializer as ts
19*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm.arm_vela import vela_compile
20*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm.operators.node_visitor import get_node_visitors
21*523fa7a6SAndroid Build Coastguard Worker
22*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm.tosa_specification import TosaSpecification
23*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm._passes.arm_pass_manager import (
24*523fa7a6SAndroid Build Coastguard Worker    ArmPassManager,
25*523fa7a6SAndroid Build Coastguard Worker)  # usort: skip
26*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm.process_node import (
27*523fa7a6SAndroid Build Coastguard Worker    process_call_function,
28*523fa7a6SAndroid Build Coastguard Worker    process_output,
29*523fa7a6SAndroid Build Coastguard Worker    process_placeholder,
30*523fa7a6SAndroid Build Coastguard Worker)
31*523fa7a6SAndroid Build Coastguard Workerfrom executorch.backends.arm.tosa_utils import dbg_fail, dbg_tosa_dump
32*523fa7a6SAndroid Build Coastguard Workerfrom executorch.exir.backend.backend_details import BackendDetails, PreprocessResult
33*523fa7a6SAndroid Build Coastguard Workerfrom executorch.exir.backend.compile_spec_schema import CompileSpec
34*523fa7a6SAndroid Build Coastguard Workerfrom torch.export.exported_program import ExportedProgram
35*523fa7a6SAndroid Build Coastguard Worker
36*523fa7a6SAndroid Build Coastguard Worker# TOSA backend debug functionality
37*523fa7a6SAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
38*523fa7a6SAndroid Build Coastguard Workerlogger.setLevel(logging.WARNING)
39*523fa7a6SAndroid Build Coastguard WorkerTOSA_DBG_VERBOSE = os.environ.get("TOSA_DBG_VERBOSE") == "1"
40*523fa7a6SAndroid Build Coastguard Workerif TOSA_DBG_VERBOSE:
41*523fa7a6SAndroid Build Coastguard Worker    logging.basicConfig(level=logging.INFO)
42*523fa7a6SAndroid Build Coastguard Worker    logger.setLevel(logging.INFO)
43*523fa7a6SAndroid Build Coastguard Worker
44*523fa7a6SAndroid Build Coastguard Worker
45*523fa7a6SAndroid Build Coastguard Workerclass ArmCompileSpecBuilder:
46*523fa7a6SAndroid Build Coastguard Worker    def __init__(self):
47*523fa7a6SAndroid Build Coastguard Worker        self.compile_spec: List[CompileSpec] = []
48*523fa7a6SAndroid Build Coastguard Worker        self.compiler_flags = []
49*523fa7a6SAndroid Build Coastguard Worker        self.output_format = None
50*523fa7a6SAndroid Build Coastguard Worker        self.path_for_intermediates = None
51*523fa7a6SAndroid Build Coastguard Worker        # TODO MLETORCH-265 Remove permute_nhwc flag
52*523fa7a6SAndroid Build Coastguard Worker        self.permute_nhwc = False
53*523fa7a6SAndroid Build Coastguard Worker        self.quantize_io = False
54*523fa7a6SAndroid Build Coastguard Worker        self.tosa_version = None
55*523fa7a6SAndroid Build Coastguard Worker
56*523fa7a6SAndroid Build Coastguard Worker    def ethosu_compile_spec(
57*523fa7a6SAndroid Build Coastguard Worker        self,
58*523fa7a6SAndroid Build Coastguard Worker        config: str,
59*523fa7a6SAndroid Build Coastguard Worker        system_config: str,
60*523fa7a6SAndroid Build Coastguard Worker        memory_mode: str,
61*523fa7a6SAndroid Build Coastguard Worker        extra_flags: Optional[str] = None,
62*523fa7a6SAndroid Build Coastguard Worker        config_ini: Optional[str] = "Arm/vela.ini",
63*523fa7a6SAndroid Build Coastguard Worker    ) -> "ArmCompileSpecBuilder":
64*523fa7a6SAndroid Build Coastguard Worker        """
65*523fa7a6SAndroid Build Coastguard Worker        Generate compile spec for Ethos-U NPU
66*523fa7a6SAndroid Build Coastguard Worker
67*523fa7a6SAndroid Build Coastguard Worker        Args:
68*523fa7a6SAndroid Build Coastguard Worker            config: Ethos-U accelerator configuration, e.g. ethos-u55-128
69*523fa7a6SAndroid Build Coastguard Worker            system_config: System configuration to select from the Vel
70*523fa7a6SAndroid Build Coastguard Worker                configuration file
71*523fa7a6SAndroid Build Coastguard Worker            memory_mode: Memory mode to select from the Vela configuration file
72*523fa7a6SAndroid Build Coastguard Worker            extra_flags: Extra flags for the Vela compiler
73*523fa7a6SAndroid Build Coastguard Worker            config_ini: Vela configuration file(s) in Python ConfigParser .ini
74*523fa7a6SAndroid Build Coastguard Worker                file format
75*523fa7a6SAndroid Build Coastguard Worker        """
76*523fa7a6SAndroid Build Coastguard Worker        assert (
77*523fa7a6SAndroid Build Coastguard Worker            self.output_format is None
78*523fa7a6SAndroid Build Coastguard Worker        ), f"Output format already set to f{self.output_format}"
79*523fa7a6SAndroid Build Coastguard Worker        self.output_format = "vela"
80*523fa7a6SAndroid Build Coastguard Worker        self.compiler_flags = [
81*523fa7a6SAndroid Build Coastguard Worker            f"--accelerator-config={config}",
82*523fa7a6SAndroid Build Coastguard Worker            f"--config={config_ini}",
83*523fa7a6SAndroid Build Coastguard Worker        ]
84*523fa7a6SAndroid Build Coastguard Worker        if system_config is not None:
85*523fa7a6SAndroid Build Coastguard Worker            self.compiler_flags.append(f"--system-config={system_config}")
86*523fa7a6SAndroid Build Coastguard Worker        if memory_mode is not None:
87*523fa7a6SAndroid Build Coastguard Worker            self.compiler_flags.append(f"--memory-mode={memory_mode}")
88*523fa7a6SAndroid Build Coastguard Worker        if extra_flags is not None:
89*523fa7a6SAndroid Build Coastguard Worker            self.compiler_flags.append(extra_flags)
90*523fa7a6SAndroid Build Coastguard Worker
91*523fa7a6SAndroid Build Coastguard Worker        base_tosa_version = "TOSA-0.80.0+BI"
92*523fa7a6SAndroid Build Coastguard Worker        if "U55" in config:
93*523fa7a6SAndroid Build Coastguard Worker            # Add the Ethos-U55 extension marker
94*523fa7a6SAndroid Build Coastguard Worker            base_tosa_version += "+u55"
95*523fa7a6SAndroid Build Coastguard Worker        self.tosa_version = TosaSpecification.create_from_string(base_tosa_version)
96*523fa7a6SAndroid Build Coastguard Worker
97*523fa7a6SAndroid Build Coastguard Worker        return self
98*523fa7a6SAndroid Build Coastguard Worker
99*523fa7a6SAndroid Build Coastguard Worker    def tosa_compile_spec(self, tosa_version: str) -> "ArmCompileSpecBuilder":
100*523fa7a6SAndroid Build Coastguard Worker        """
101*523fa7a6SAndroid Build Coastguard Worker        Generate compile spec for TOSA flatbuffer output
102*523fa7a6SAndroid Build Coastguard Worker        """
103*523fa7a6SAndroid Build Coastguard Worker        assert (
104*523fa7a6SAndroid Build Coastguard Worker            self.output_format is None
105*523fa7a6SAndroid Build Coastguard Worker        ), f"Output format already set: {self.output_format}"
106*523fa7a6SAndroid Build Coastguard Worker        self.output_format = "tosa"
107*523fa7a6SAndroid Build Coastguard Worker        self.tosa_version = TosaSpecification.create_from_string(tosa_version)
108*523fa7a6SAndroid Build Coastguard Worker        return self
109*523fa7a6SAndroid Build Coastguard Worker
110*523fa7a6SAndroid Build Coastguard Worker    def dump_intermediate_artifacts_to(
111*523fa7a6SAndroid Build Coastguard Worker        self, output_path: str
112*523fa7a6SAndroid Build Coastguard Worker    ) -> "ArmCompileSpecBuilder":
113*523fa7a6SAndroid Build Coastguard Worker        """
114*523fa7a6SAndroid Build Coastguard Worker        Sets a path for dumping intermediate results during such as tosa and pte.
115*523fa7a6SAndroid Build Coastguard Worker        """
116*523fa7a6SAndroid Build Coastguard Worker        self.path_for_intermediates = output_path
117*523fa7a6SAndroid Build Coastguard Worker        return self
118*523fa7a6SAndroid Build Coastguard Worker
119*523fa7a6SAndroid Build Coastguard Worker    def set_permute_memory_format(
120*523fa7a6SAndroid Build Coastguard Worker        self, set_nhwc_permutation: bool = True
121*523fa7a6SAndroid Build Coastguard Worker    ) -> "ArmCompileSpecBuilder":
122*523fa7a6SAndroid Build Coastguard Worker        """
123*523fa7a6SAndroid Build Coastguard Worker        Permute to channel last in compiler and runtime. Compilation and
124*523fa7a6SAndroid Build Coastguard Worker        runtime will convert rank 4 inputs to channel last for each sub-graph.
125*523fa7a6SAndroid Build Coastguard Worker        """
126*523fa7a6SAndroid Build Coastguard Worker        self.permute_nhwc = set_nhwc_permutation
127*523fa7a6SAndroid Build Coastguard Worker        return self
128*523fa7a6SAndroid Build Coastguard Worker
129*523fa7a6SAndroid Build Coastguard Worker    def set_quantize_io(self, quantize_io: bool = False) -> "ArmCompileSpecBuilder":
130*523fa7a6SAndroid Build Coastguard Worker        """
131*523fa7a6SAndroid Build Coastguard Worker        Quantization of inputs and dequantization of outputs for cases where
132*523fa7a6SAndroid Build Coastguard Worker        whole graph is quantized and method signature is not of quantized type.
133*523fa7a6SAndroid Build Coastguard Worker        """
134*523fa7a6SAndroid Build Coastguard Worker        self.quantize_io = quantize_io
135*523fa7a6SAndroid Build Coastguard Worker        return self
136*523fa7a6SAndroid Build Coastguard Worker
137*523fa7a6SAndroid Build Coastguard Worker    def build(self) -> List[CompileSpec]:
138*523fa7a6SAndroid Build Coastguard Worker        """
139*523fa7a6SAndroid Build Coastguard Worker        Generate a list of compile spec objects from the builder
140*523fa7a6SAndroid Build Coastguard Worker        """
141*523fa7a6SAndroid Build Coastguard Worker        assert self.tosa_version
142*523fa7a6SAndroid Build Coastguard Worker
143*523fa7a6SAndroid Build Coastguard Worker        # Always supply a TOSA version
144*523fa7a6SAndroid Build Coastguard Worker        self.compile_spec = [
145*523fa7a6SAndroid Build Coastguard Worker            CompileSpec("tosa_version", str(self.tosa_version).encode())
146*523fa7a6SAndroid Build Coastguard Worker        ]
147*523fa7a6SAndroid Build Coastguard Worker
148*523fa7a6SAndroid Build Coastguard Worker        if self.output_format == "vela":
149*523fa7a6SAndroid Build Coastguard Worker            self.compile_spec += [
150*523fa7a6SAndroid Build Coastguard Worker                CompileSpec("output_format", "vela".encode()),
151*523fa7a6SAndroid Build Coastguard Worker                CompileSpec("compile_flags", " ".join(self.compiler_flags).encode()),
152*523fa7a6SAndroid Build Coastguard Worker            ]
153*523fa7a6SAndroid Build Coastguard Worker        elif self.output_format == "tosa":
154*523fa7a6SAndroid Build Coastguard Worker            self.compile_spec.append(CompileSpec("output_format", "tosa".encode()))
155*523fa7a6SAndroid Build Coastguard Worker
156*523fa7a6SAndroid Build Coastguard Worker        if self.path_for_intermediates is not None:
157*523fa7a6SAndroid Build Coastguard Worker            self.compile_spec.append(
158*523fa7a6SAndroid Build Coastguard Worker                CompileSpec("debug_artifact_path", self.path_for_intermediates.encode())
159*523fa7a6SAndroid Build Coastguard Worker            )
160*523fa7a6SAndroid Build Coastguard Worker
161*523fa7a6SAndroid Build Coastguard Worker        if self.permute_nhwc:
162*523fa7a6SAndroid Build Coastguard Worker            self.compile_spec.append(
163*523fa7a6SAndroid Build Coastguard Worker                CompileSpec("permute_memory_format", "nhwc".encode())
164*523fa7a6SAndroid Build Coastguard Worker            )
165*523fa7a6SAndroid Build Coastguard Worker
166*523fa7a6SAndroid Build Coastguard Worker        if self.quantize_io:
167*523fa7a6SAndroid Build Coastguard Worker            self.compile_spec.append(CompileSpec("quantize_io", "True".encode()))
168*523fa7a6SAndroid Build Coastguard Worker
169*523fa7a6SAndroid Build Coastguard Worker        return self.compile_spec
170*523fa7a6SAndroid Build Coastguard Worker
171*523fa7a6SAndroid Build Coastguard Worker
172*523fa7a6SAndroid Build Coastguard Workerdef is_permute_memory(compile_spec: List[CompileSpec]) -> bool:
173*523fa7a6SAndroid Build Coastguard Worker    for spec in compile_spec:
174*523fa7a6SAndroid Build Coastguard Worker        if spec.key == "permute_memory_format":
175*523fa7a6SAndroid Build Coastguard Worker            return spec.value.decode() == "nhwc"
176*523fa7a6SAndroid Build Coastguard Worker    return False
177*523fa7a6SAndroid Build Coastguard Worker
178*523fa7a6SAndroid Build Coastguard Worker
179*523fa7a6SAndroid Build Coastguard Workerdef is_tosa(compile_spec: List[CompileSpec]) -> bool:
180*523fa7a6SAndroid Build Coastguard Worker    for spec in compile_spec:
181*523fa7a6SAndroid Build Coastguard Worker        if spec.key == "output_format":
182*523fa7a6SAndroid Build Coastguard Worker            return spec.value.decode() == "tosa"
183*523fa7a6SAndroid Build Coastguard Worker    return False
184*523fa7a6SAndroid Build Coastguard Worker
185*523fa7a6SAndroid Build Coastguard Worker
186*523fa7a6SAndroid Build Coastguard Workerdef get_intermediate_path(compile_spec: List[CompileSpec]) -> Optional[str]:
187*523fa7a6SAndroid Build Coastguard Worker    for spec in compile_spec:
188*523fa7a6SAndroid Build Coastguard Worker        if spec.key == "debug_artifact_path":
189*523fa7a6SAndroid Build Coastguard Worker            return spec.value.decode()
190*523fa7a6SAndroid Build Coastguard Worker    return None
191*523fa7a6SAndroid Build Coastguard Worker
192*523fa7a6SAndroid Build Coastguard Worker
193*523fa7a6SAndroid Build Coastguard Workerdef _get_first_delegation_tag(graph_module) -> str | None:
194*523fa7a6SAndroid Build Coastguard Worker    """Get the first delegation tag from the graph_module or return None."""
195*523fa7a6SAndroid Build Coastguard Worker    for node in graph_module.graph.nodes:
196*523fa7a6SAndroid Build Coastguard Worker        tag = node.meta.get("delegation_tag")
197*523fa7a6SAndroid Build Coastguard Worker        if tag:
198*523fa7a6SAndroid Build Coastguard Worker            return tag
199*523fa7a6SAndroid Build Coastguard Worker
200*523fa7a6SAndroid Build Coastguard Worker    logger.debug("No delegation tag found in partition.")
201*523fa7a6SAndroid Build Coastguard Worker    return None
202*523fa7a6SAndroid Build Coastguard Worker
203*523fa7a6SAndroid Build Coastguard Worker
204*523fa7a6SAndroid Build Coastguard Worker@final
205*523fa7a6SAndroid Build Coastguard Workerclass ArmBackend(BackendDetails):
206*523fa7a6SAndroid Build Coastguard Worker    @staticmethod
207*523fa7a6SAndroid Build Coastguard Worker    def preprocess(  # noqa: C901
208*523fa7a6SAndroid Build Coastguard Worker        edge_program: ExportedProgram,
209*523fa7a6SAndroid Build Coastguard Worker        compile_spec: List[CompileSpec],
210*523fa7a6SAndroid Build Coastguard Worker    ) -> PreprocessResult:
211*523fa7a6SAndroid Build Coastguard Worker        logger.info("ArmBackend::preprocess")
212*523fa7a6SAndroid Build Coastguard Worker
213*523fa7a6SAndroid Build Coastguard Worker        # if a debug/test build capture output files from TOSA stage
214*523fa7a6SAndroid Build Coastguard Worker        artifact_path = None
215*523fa7a6SAndroid Build Coastguard Worker        output_format = ""
216*523fa7a6SAndroid Build Coastguard Worker        compile_flags = []
217*523fa7a6SAndroid Build Coastguard Worker        for spec in compile_spec:
218*523fa7a6SAndroid Build Coastguard Worker            if spec.key == "debug_artifact_path":
219*523fa7a6SAndroid Build Coastguard Worker                artifact_path = spec.value.decode()
220*523fa7a6SAndroid Build Coastguard Worker            if spec.key == "output_format":
221*523fa7a6SAndroid Build Coastguard Worker                output_format = spec.value.decode()
222*523fa7a6SAndroid Build Coastguard Worker            if spec.key == "compile_flags":
223*523fa7a6SAndroid Build Coastguard Worker                compile_flags.append(spec.value.decode())
224*523fa7a6SAndroid Build Coastguard Worker
225*523fa7a6SAndroid Build Coastguard Worker        # Check that the output format is set in the compile spec
226*523fa7a6SAndroid Build Coastguard Worker        if not output_format:
227*523fa7a6SAndroid Build Coastguard Worker            raise RuntimeError("output format is required")
228*523fa7a6SAndroid Build Coastguard Worker
229*523fa7a6SAndroid Build Coastguard Worker        tosa_spec = TosaSpecification.create_from_compilespecs(compile_spec)
230*523fa7a6SAndroid Build Coastguard Worker        assert (
231*523fa7a6SAndroid Build Coastguard Worker            tosa_spec is not None
232*523fa7a6SAndroid Build Coastguard Worker        ), "TOSA backend needs a TOSA version specified in the CompileSpec!"
233*523fa7a6SAndroid Build Coastguard Worker
234*523fa7a6SAndroid Build Coastguard Worker        if output_format == "vela" and len(compile_flags) == 0:
235*523fa7a6SAndroid Build Coastguard Worker            # Not testing for compile_flags correctness here, just that they are
236*523fa7a6SAndroid Build Coastguard Worker            # present. The compiler will give errors if they are not valid.
237*523fa7a6SAndroid Build Coastguard Worker            raise RuntimeError("compile flags are required for vela output format")
238*523fa7a6SAndroid Build Coastguard Worker
239*523fa7a6SAndroid Build Coastguard Worker        logger.info(f"Converting ExportedProgram to TOSA: {tosa_spec}")
240*523fa7a6SAndroid Build Coastguard Worker
241*523fa7a6SAndroid Build Coastguard Worker        # Converted output for this subgraph, serializer needs path early as it emits
242*523fa7a6SAndroid Build Coastguard Worker        # const data directly. Path created and data written only in debug builds.
243*523fa7a6SAndroid Build Coastguard Worker        tosa_graph = ts.TosaSerializer(artifact_path)
244*523fa7a6SAndroid Build Coastguard Worker        graph_module = ArmPassManager().transform_to_backend_pipeline(
245*523fa7a6SAndroid Build Coastguard Worker            exported_program=edge_program, compile_spec=compile_spec
246*523fa7a6SAndroid Build Coastguard Worker        )
247*523fa7a6SAndroid Build Coastguard Worker
248*523fa7a6SAndroid Build Coastguard Worker        node_visitors = get_node_visitors(edge_program, tosa_spec)
249*523fa7a6SAndroid Build Coastguard Worker
250*523fa7a6SAndroid Build Coastguard Worker        for node in graph_module.graph.nodes:
251*523fa7a6SAndroid Build Coastguard Worker            if node.op == "call_function":
252*523fa7a6SAndroid Build Coastguard Worker                process_call_function(node, tosa_graph, node_visitors, tosa_spec)
253*523fa7a6SAndroid Build Coastguard Worker            elif node.op == "placeholder":
254*523fa7a6SAndroid Build Coastguard Worker                process_placeholder(node, tosa_graph, edge_program, tosa_spec)
255*523fa7a6SAndroid Build Coastguard Worker            elif node.op == "output":
256*523fa7a6SAndroid Build Coastguard Worker                process_output(node, tosa_graph)
257*523fa7a6SAndroid Build Coastguard Worker            else:
258*523fa7a6SAndroid Build Coastguard Worker                # This will only happen if an unpartitioned graph is passed without
259*523fa7a6SAndroid Build Coastguard Worker                # any checking of compatibility.
260*523fa7a6SAndroid Build Coastguard Worker                dbg_fail(node, tosa_graph, artifact_path)
261*523fa7a6SAndroid Build Coastguard Worker
262*523fa7a6SAndroid Build Coastguard Worker        # TODO: It would be awesome if this dump could somehow be done on top level and not here.
263*523fa7a6SAndroid Build Coastguard Worker        # Problem is that the desc.json has to be created on the tosa_graph object, which we can't
264*523fa7a6SAndroid Build Coastguard Worker        # access from top level.
265*523fa7a6SAndroid Build Coastguard Worker        if artifact_path:
266*523fa7a6SAndroid Build Coastguard Worker            tag = _get_first_delegation_tag(graph_module)
267*523fa7a6SAndroid Build Coastguard Worker            dbg_tosa_dump(
268*523fa7a6SAndroid Build Coastguard Worker                tosa_graph,
269*523fa7a6SAndroid Build Coastguard Worker                artifact_path,
270*523fa7a6SAndroid Build Coastguard Worker                suffix="{}".format(f"_{tag}" if tag else ""),
271*523fa7a6SAndroid Build Coastguard Worker            )
272*523fa7a6SAndroid Build Coastguard Worker
273*523fa7a6SAndroid Build Coastguard Worker        # Serialize and return the program. While we have always produced TOSA
274*523fa7a6SAndroid Build Coastguard Worker        # output as an intermediate, some flows compile to device binaries in
275*523fa7a6SAndroid Build Coastguard Worker        # preprocess and some consume TOSA fb directly.
276*523fa7a6SAndroid Build Coastguard Worker        if output_format == "vela":
277*523fa7a6SAndroid Build Coastguard Worker            # Emit vela_bin_stream format
278*523fa7a6SAndroid Build Coastguard Worker            binary = vela_compile(tosa_graph, compile_flags)
279*523fa7a6SAndroid Build Coastguard Worker        elif output_format == "tosa":
280*523fa7a6SAndroid Build Coastguard Worker            # Emit TOSA flatbuffer
281*523fa7a6SAndroid Build Coastguard Worker            binary = bytes(tosa_graph.serialize())
282*523fa7a6SAndroid Build Coastguard Worker        else:
283*523fa7a6SAndroid Build Coastguard Worker            raise RuntimeError(f"Unknown format {output_format}")
284*523fa7a6SAndroid Build Coastguard Worker
285*523fa7a6SAndroid Build Coastguard Worker        # Continueing from above. Can I put tosa_graph into this function?
286*523fa7a6SAndroid Build Coastguard Worker        # debug_handle_map = ...
287*523fa7a6SAndroid Build Coastguard Worker        return PreprocessResult(processed_bytes=binary)
288