Skip to content

Commit 6d4bc1f

Browse files
committed
Arm backend: Add complie spec factories
Signed-off-by: Erik Lundell <[email protected]> Change-Id: I46f84fb3772b2e34a69c70dc2c24bbdebd8a05d9
1 parent f6a5fa0 commit 6d4bc1f

File tree

5 files changed

+101
-96
lines changed

5 files changed

+101
-96
lines changed

backends/arm/test/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import pytest
1616
from executorch.backends.arm.ethosu import EthosUCompileSpec
17+
1718
from executorch.backends.arm.test.runner_utils import (
1819
arm_executor_runner_exists,
1920
corstone300_installed,

backends/arm/test/tester/arm_tester.py

Lines changed: 30 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,11 @@
2828

2929
import torch.fx
3030
import torch.utils._pytree as pytree
31-
3231
from executorch.backends.arm._passes.arm_pass_manager import ArmPassManager
3332

3433
from executorch.backends.arm.common.arm_compile_spec import ArmCompileSpec
35-
from executorch.backends.arm.ethosu import EthosUCompileSpec, EthosUPartitioner
36-
from executorch.backends.arm.quantizer import (
37-
EthosUQuantizer,
38-
get_symmetric_quantization_config,
39-
TOSAQuantizer,
40-
VgfQuantizer,
41-
)
34+
from executorch.backends.arm.ethosu import EthosUCompileSpec
35+
from executorch.backends.arm.quantizer import get_symmetric_quantization_config
4236
from executorch.backends.arm.test.runner_utils import (
4337
dbg_tosa_fb_to_json,
4438
get_output_quantization_params,
@@ -53,9 +47,13 @@
5347
from executorch.backends.arm.tosa import TosaSpecification
5448
from executorch.backends.arm.tosa.compile_spec import TosaCompileSpec
5549
from executorch.backends.arm.tosa.mapping import extract_tensor_meta
56-
from executorch.backends.arm.tosa.partitioner import TOSAPartitioner
5750

58-
from executorch.backends.arm.vgf import VgfCompileSpec, VgfPartitioner
51+
from executorch.backends.arm.util._factory import (
52+
create_partitioner,
53+
create_quantizer,
54+
parse_compile_spec,
55+
)
56+
from executorch.backends.arm.vgf import VgfCompileSpec
5957

6058
from executorch.backends.test.harness.stages import Stage, StageType
6159
from executorch.backends.xnnpack.test.tester import Tester
@@ -82,7 +80,6 @@
8280
_copy_module,
8381
_update_exported_program_graph_module,
8482
)
85-
8683
from tabulate import tabulate
8784

8885
from torch.export.graph_signature import ExportGraphSignature, InputSpec, OutputSpec
@@ -102,26 +99,20 @@ def _dump_lowered_modules_artifact(
10299
artifact.exported_program().graph_signature
103100
)
104101

105-
def get_output_format(lowered_module) -> str | None:
106-
for spec in lowered_module.compile_specs:
107-
if spec.key == "output_format":
108-
return spec.value.decode()
109-
return None
110-
111102
for node in graph_module.graph.nodes:
112103
if node.op == "get_attr" and node.name.startswith("lowered_module_"):
113104
lowered_module = getattr(graph_module, node.name)
114105
assert isinstance(
115106
lowered_module, LoweredBackendModule
116107
), f"Attribute {node.name} must be of type LoweredBackendModule."
117108

118-
output_format = get_output_format(lowered_module)
119-
if output_format == "tosa":
109+
compile_spec = parse_compile_spec(lowered_module.compile_specs)
110+
if isinstance(compile_spec, TosaCompileSpec):
120111
tosa_fb = lowered_module.processed_bytes
121112
to_print = dbg_tosa_fb_to_json(tosa_fb)
122113
to_print = pformat(to_print, compact=True, indent=1)
123114
output += f"\nTOSA deserialized {node.name}: \n{to_print}\n"
124-
elif output_format == EthosUCompileSpec.get_output_format():
115+
elif isinstance(compile_spec, EthosUCompileSpec):
125116
vela_cmd_stream = lowered_module.processed_bytes
126117
output += f"\nVela command stream {node.name}: \n{vela_cmd_stream}\n"
127118
else:
@@ -283,13 +274,7 @@ def quantize(
283274
quantize_stage: Optional[tester.Quantize] = None,
284275
):
285276
if quantize_stage is None:
286-
quantizer = None
287-
if isinstance(self.compile_spec, TosaCompileSpec):
288-
quantizer = TOSAQuantizer(self.compile_spec)
289-
elif isinstance(self.compile_spec, EthosUCompileSpec):
290-
quantizer = EthosUQuantizer(self.compile_spec)
291-
elif isinstance(self.compile_spec, VgfCompileSpec):
292-
quantizer = VgfQuantizer(self.compile_spec)
277+
quantizer = create_quantizer(self.compile_spec)
293278
quantize_stage = tester.Quantize(
294279
quantizer,
295280
get_symmetric_quantization_config(),
@@ -311,14 +296,7 @@ def to_edge(
311296

312297
def partition(self, partition_stage: Optional[Partition] = None):
313298
if partition_stage is None:
314-
if isinstance(self.compile_spec, TosaCompileSpec):
315-
arm_partitioner = TOSAPartitioner(self.compile_spec)
316-
elif isinstance(self.compile_spec, EthosUCompileSpec):
317-
arm_partitioner = EthosUPartitioner(self.compile_spec)
318-
elif isinstance(self.compile_spec, VgfCompileSpec):
319-
arm_partitioner = VgfPartitioner(self.compile_spec)
320-
else:
321-
raise ValueError("compile spec doesn't target any Arm Partitioner")
299+
arm_partitioner = create_partitioner(self.compile_spec)
322300
partition_stage = Partition(arm_partitioner)
323301
return super().partition(partition_stage)
324302

@@ -328,7 +306,7 @@ def to_edge_transform_and_lower(
328306
partitioners: Optional[List[Partitioner]] = None,
329307
edge_compile_config: Optional[EdgeCompileConfig] = None,
330308
additional_checks: Optional[
331-
List[Union[DontPartition | DontPartitionModule | DontPartitionName]]
309+
List[DontPartition | DontPartitionModule | DontPartitionName]
332310
] = None,
333311
transform_passes: Optional[
334312
Union[Sequence[PassType], Dict[str, Sequence[PassType]]]
@@ -341,20 +319,9 @@ def to_edge_transform_and_lower(
341319

342320
if to_edge_and_lower_stage is None:
343321
if partitioners is None:
344-
if isinstance(self.compile_spec, TosaCompileSpec):
345-
arm_partitioner = TOSAPartitioner(
346-
self.compile_spec, additional_checks
347-
)
348-
elif isinstance(self.compile_spec, EthosUCompileSpec):
349-
arm_partitioner = EthosUPartitioner(
350-
self.compile_spec, additional_checks
351-
)
352-
elif isinstance(self.compile_spec, VgfCompileSpec):
353-
arm_partitioner = VgfPartitioner(
354-
self.compile_spec, additional_checks
355-
)
356-
else:
357-
raise ValueError("compile spec doesn't target any Arm Partitioner")
322+
arm_partitioner = create_partitioner(
323+
self.compile_spec, additional_checks
324+
)
358325
partitioners = [arm_partitioner]
359326
to_edge_and_lower_stage = ToEdgeTransformAndLower(
360327
partitioners,
@@ -731,22 +698,19 @@ def _get_tosa_operator_distribution(
731698
op_list = []
732699
id = 0
733700
while lowered_module := getattr(graph_module, f"lowered_module_{id}", None):
734-
for spec in lowered_module.compile_specs:
735-
if spec.key != "output_format":
736-
continue
737-
if spec.value == b"tosa":
738-
tosa_fb = lowered_module.processed_bytes
739-
tosa_json = dbg_tosa_fb_to_json(tosa_fb)
740-
for region in tosa_json["regions"]:
741-
for block in region["blocks"]:
742-
op_list.extend(
743-
[operator["op"] for operator in block["operators"]]
744-
)
745-
break
746-
elif spec.value == EthosUCompileSpec.get_output_format().encode():
747-
return "Can not get operator distribution for Vela command stream."
748-
else:
749-
return f"Unknown output format '{spec.value}'."
701+
compile_spec = parse_compile_spec(lowered_module.compile_specs)
702+
if isinstance(compile_spec, TosaCompileSpec):
703+
tosa_fb = lowered_module.processed_bytes
704+
tosa_json = dbg_tosa_fb_to_json(tosa_fb)
705+
for region in tosa_json["regions"]:
706+
for block in region["blocks"]:
707+
op_list.extend([operator["op"] for operator in block["operators"]])
708+
elif isinstance(compile_spec, EthosUCompileSpec):
709+
return "Can not get operator distribution for Vela command stream."
710+
elif isinstance(compile_spec, VgfCompileSpec):
711+
return "Can not get operator distribution for VGF."
712+
else:
713+
return f"Unknown output format '{compile_spec.get_output_format()}'."
750714
id += 1
751715
if id == 0:
752716
return "No delegate with name 'lowered_module_0 found in graph module."

backends/arm/tosa/backend.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ def filter_tosa_compile_specs(
201201
hardware.
202202
"""
203203

204-
new_compile_spec = TosaCompileSpec.__new__(TosaCompileSpec)
205-
new_compile_spec._set_compile_specs(
206-
compile_spec.tosa_spec, [], compile_spec.get_intermediate_path()
204+
return (
205+
TosaCompileSpec(compile_spec.tosa_spec)
206+
.dump_intermediate_artifacts_to(compile_spec.get_intermediate_path())
207+
.dump_debug_info(compile_spec.tosa_debug_mode)
207208
)
208-
return new_compile_spec

backends/arm/util/_factory.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Copyright 2025 Arm Limited and/or its affiliates.
2+
#
3+
# This source code is licensed under the BSD-style license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
from executorch.backends.arm.common.arm_compile_spec import ArmCompileSpec
7+
from executorch.backends.arm.ethosu import EthosUCompileSpec, EthosUPartitioner
8+
from executorch.backends.arm.quantizer import (
9+
EthosUQuantizer,
10+
TOSAQuantizer,
11+
VgfQuantizer,
12+
)
13+
from executorch.backends.arm.tosa.compile_spec import TosaCompileSpec
14+
from executorch.backends.arm.tosa.partitioner import TOSAPartitioner
15+
from executorch.backends.arm.vgf import VgfCompileSpec, VgfPartitioner
16+
from executorch.exir.backend.compile_spec_schema import CompileSpec
17+
from torch.fx.passes.operator_support import OperatorSupportBase
18+
19+
20+
def parse_compile_spec(compile_specs: list[CompileSpec]) -> ArmCompileSpec:
21+
output_format = None
22+
for spec in compile_specs:
23+
if spec.key == "output_format":
24+
output_format = spec.value.decode()
25+
break
26+
else:
27+
raise ValueError("Compile spec without output format.")
28+
if output_format == TosaCompileSpec.get_output_format():
29+
return TosaCompileSpec.from_list(compile_specs)
30+
if output_format == EthosUCompileSpec.get_output_format():
31+
return EthosUCompileSpec.from_list(compile_specs)
32+
if output_format == VgfCompileSpec.get_output_format():
33+
return VgfCompileSpec.from_list(compile_specs)
34+
raise ValueError(f"Unknown output format {output_format}")
35+
36+
37+
def create_partitioner(
38+
compile_spec: ArmCompileSpec,
39+
additional_checks: list[OperatorSupportBase] | None = None,
40+
):
41+
if isinstance(compile_spec, TosaCompileSpec):
42+
return TOSAPartitioner(compile_spec, additional_checks)
43+
elif isinstance(compile_spec, EthosUCompileSpec):
44+
return EthosUPartitioner(compile_spec, additional_checks)
45+
elif isinstance(compile_spec, VgfCompileSpec):
46+
return VgfPartitioner(compile_spec, additional_checks)
47+
else:
48+
raise ValueError("compile spec doesn't target any Arm Partitioner")
49+
50+
51+
def create_quantizer(compile_spec: ArmCompileSpec):
52+
if isinstance(compile_spec, TosaCompileSpec):
53+
return TOSAQuantizer(compile_spec)
54+
elif isinstance(compile_spec, EthosUCompileSpec):
55+
return EthosUQuantizer(compile_spec)
56+
elif isinstance(compile_spec, VgfCompileSpec):
57+
return VgfQuantizer(compile_spec)
58+
else:
59+
raise ValueError("compile spec doesn't target any Arm Quantizer")

examples/arm/aot_arm_compiler.py

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,18 @@
1919
import torch
2020
from examples.devtools.scripts.export_bundled_program import save_bundled_program
2121
from executorch.backends.arm.common.arm_compile_spec import ArmCompileSpec
22-
from executorch.backends.arm.ethosu import EthosUCompileSpec, EthosUPartitioner
23-
from executorch.backends.arm.quantizer import (
24-
EthosUQuantizer,
25-
get_symmetric_quantization_config,
26-
TOSAQuantizer,
27-
VgfQuantizer,
28-
)
22+
from executorch.backends.arm.ethosu import EthosUCompileSpec
23+
from executorch.backends.arm.quantizer import get_symmetric_quantization_config
2924
from executorch.backends.arm.tosa import TosaSpecification
3025
from executorch.backends.arm.tosa.compile_spec import TosaCompileSpec
31-
from executorch.backends.arm.tosa.partitioner import TOSAPartitioner
26+
from executorch.backends.arm.util._factory import create_partitioner, create_quantizer
3227

3328
from executorch.backends.arm.util.arm_model_evaluator import (
3429
GenericModelEvaluator,
3530
MobileNetV2Evaluator,
3631
)
3732

38-
from executorch.backends.arm.vgf import VgfCompileSpec, VgfPartitioner
33+
from executorch.backends.arm.vgf import VgfCompileSpec
3934

4035
# To use Cortex-M backend
4136
from executorch.backends.cortex_m.passes.quantized_op_fusion_pass import (
@@ -151,15 +146,8 @@ def quantize(
151146
"""This is the official recommended flow for quantization in pytorch 2.0 export"""
152147
logging.info("Quantizing Model...")
153148
logging.debug(f"Original model: {model}")
154-
quantizer = None
155-
if isinstance(compile_specs, EthosUCompileSpec):
156-
quantizer = EthosUQuantizer(compile_specs)
157-
elif isinstance(compile_specs, TosaCompileSpec):
158-
quantizer = TOSAQuantizer(compile_specs)
159-
elif isinstance(compile_specs, VgfCompileSpec):
160-
quantizer = VgfQuantizer(compile_specs)
161-
else:
162-
raise RuntimeError("Unsupported compilespecs for quantization!")
149+
150+
quantizer = create_quantizer(compile_specs)
163151

164152
operator_config = get_symmetric_quantization_config()
165153
quantizer.set_global(operator_config)
@@ -757,14 +745,7 @@ def to_edge_TOSA_delegate(
757745
)
758746
model = model_int8
759747

760-
if isinstance(compile_spec, EthosUCompileSpec):
761-
partitioner = EthosUPartitioner(compile_spec)
762-
elif isinstance(compile_spec, TosaCompileSpec):
763-
partitioner = TOSAPartitioner(compile_spec)
764-
elif isinstance(compile_spec, VgfCompileSpec):
765-
partitioner = VgfPartitioner(compile_spec)
766-
else:
767-
raise RuntimeError(f"Unhandled compile spec: {compile_spec}")
748+
partitioner = create_partitioner(compile_spec)
768749

769750
edge = to_edge_transform_and_lower(
770751
exported_program,

0 commit comments

Comments
 (0)