Skip to content

Commit 0cc729e

Browse files
Merge branch 'main' into change-1119426
2 parents 7f29bcd + 0145604 commit 0cc729e

18 files changed

+439
-254
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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 typing import Tuple
7+
8+
import torch
9+
import torch.nn as nn
10+
11+
from executorch.backends.arm.test import common
12+
from executorch.backends.arm.test.tester.test_pipeline import (
13+
EthosU55PipelineINT,
14+
EthosU85PipelineINT,
15+
TosaPipelineFP,
16+
TosaPipelineINT,
17+
VgfPipeline,
18+
)
19+
20+
test_data_suite = {
21+
# (test_name, test_data)
22+
"ones_two_tensors": lambda: ((torch.ones(1), torch.ones(1)), 0),
23+
"ones_and_rand_three_tensors": lambda: (
24+
(torch.ones(1, 2), torch.randn(1, 2), torch.randn(1, 2)),
25+
1,
26+
),
27+
"ones_and_rand_four_tensors": lambda: (
28+
(
29+
torch.ones(1, 2, 5),
30+
torch.randn(1, 2, 5),
31+
torch.randn(1, 2, 5),
32+
torch.randn(1, 2, 5),
33+
),
34+
-1,
35+
),
36+
"rand_two_tensors": lambda: (
37+
(torch.randn(2, 2, 4), torch.randn(2, 2, 4)),
38+
2,
39+
),
40+
"rand_two_tensors_dim_0": lambda: (
41+
(torch.randn(1, 2, 4, 4), torch.randn(1, 2, 4, 4)),
42+
),
43+
"rand_two_tensors_dim_2": lambda: (
44+
(torch.randn(2, 2, 3, 5), torch.randn(2, 2, 3, 5)),
45+
2,
46+
),
47+
"rand_large": lambda: (
48+
(
49+
10000 * torch.randn(2, 3, 1, 4),
50+
torch.randn(2, 3, 1, 4),
51+
torch.randn(2, 3, 1, 4),
52+
),
53+
-3,
54+
),
55+
}
56+
57+
58+
class Stack(nn.Module):
59+
aten_op = "torch.ops.aten.stack.default"
60+
exir_op = "executorch_exir_dialects_edge__ops_aten_cat_default"
61+
62+
def forward(self, n: tuple[torch.Tensor, ...], dim: int = 0):
63+
return torch.stack(n, dim)
64+
65+
66+
input_t1 = Tuple[torch.Tensor]
67+
68+
69+
@common.parametrize("test_module", test_data_suite)
70+
def test_stack_tosa_FP(test_module: input_t1):
71+
test_data = test_module()
72+
pipeline = TosaPipelineFP[input_t1](
73+
Stack(),
74+
test_data,
75+
aten_op=Stack.aten_op,
76+
exir_op=Stack.exir_op,
77+
use_to_edge_transform_and_lower=False,
78+
)
79+
pipeline.run()
80+
81+
82+
@common.parametrize("test_module", test_data_suite)
83+
def test_stack_tosa_INT(test_module: input_t1):
84+
test_data = test_module()
85+
pipeline = TosaPipelineINT[input_t1](
86+
Stack(),
87+
test_data,
88+
aten_op=Stack.aten_op,
89+
exir_op=Stack.exir_op,
90+
use_to_edge_transform_and_lower=False,
91+
)
92+
pipeline.run()
93+
94+
95+
@common.XfailIfNoCorstone300
96+
@common.parametrize("test_module", test_data_suite)
97+
def test_stack_u55_INT(test_module: input_t1):
98+
test_data = test_module()
99+
pipeline = EthosU55PipelineINT[input_t1](
100+
Stack(),
101+
test_data,
102+
aten_ops=Stack.aten_op,
103+
exir_ops=Stack.exir_op,
104+
use_to_edge_transform_and_lower=False,
105+
)
106+
pipeline.run()
107+
108+
109+
@common.XfailIfNoCorstone320
110+
@common.parametrize("test_module", test_data_suite)
111+
def test_stack_u85_INT(test_module: input_t1):
112+
test_data = test_module()
113+
pipeline = EthosU85PipelineINT[input_t1](
114+
Stack(),
115+
test_data,
116+
aten_ops=Stack.aten_op,
117+
exir_ops=Stack.exir_op,
118+
use_to_edge_transform_and_lower=False,
119+
)
120+
pipeline.run()
121+
122+
123+
@common.SkipIfNoModelConverter
124+
@common.parametrize("test_module", test_data_suite)
125+
def test_stack_vgf_FP(test_module: input_t1):
126+
test_data = test_module()
127+
pipeline = VgfPipeline[input_t1](
128+
Stack(),
129+
test_data,
130+
aten_op=Stack.aten_op,
131+
exir_op=Stack.exir_op,
132+
tosa_version="TOSA-1.0+FP",
133+
use_to_edge_transform_and_lower=False,
134+
)
135+
pipeline.run()
136+
137+
138+
@common.SkipIfNoModelConverter
139+
@common.parametrize("test_module", test_data_suite)
140+
def test_stack_vgf_INT(test_module: input_t1):
141+
test_data = test_module()
142+
pipeline = VgfPipeline[input_t1](
143+
Stack(),
144+
test_data,
145+
aten_op=Stack.aten_op,
146+
exir_op=Stack.exir_op,
147+
tosa_version="TOSA-1.0+INT",
148+
use_to_edge_transform_and_lower=False,
149+
)
150+
pipeline.run()

backends/nxp/backend/edge_program_converter.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from torch.fx import Node
1919
from torch.nn.parameter import Parameter
2020
from executorch.backends.nxp.backend.ir.converter.node_converters.ops_converters import * # noqa F403
21+
from executorch.backends.nxp.backend.neutron_target_spec import NeutronTargetSpec
2122
from executorch.backends.nxp.backend.node_format_inference import (
2223
NodeFormat,
2324
NodeFormatInference,
@@ -54,19 +55,22 @@ class EdgeProgramToIRConverter:
5455
"""
5556

5657
_default_conversion_config = ConversionConfig()
58+
_default_target_spec = NeutronTargetSpec("imxrt700", "SDK_25_09")
5759
_default_delegation_options = CustomDelegationOptions()
5860

5961
def convert_program(
6062
self,
6163
edge_program: ExportedProgram,
62-
conversion_config=_default_conversion_config,
64+
conversion_config: ConversionConfig = _default_conversion_config,
65+
neutron_target_spec: NeutronTargetSpec = _default_target_spec,
6366
custom_delegation_options: CustomDelegationOptions = _default_delegation_options,
6467
) -> (bytes, dict):
6568
"""
6669
Convert ExportedProgram in Edge dialect to IR (TFLite flatbuffers) as bytes.
6770
6871
:param edge_program: Converter ExportedProgram.
6972
:param conversion_config: ConversionConfig instance.
73+
:param neutron_target_spec: Object for querying the target platform to retrieve its properties.
7074
:param custom_delegation_options: Custom user options which affect node delegation.
7175
:return: TFLite flatbuffers as bytes.
7276
"""
@@ -76,6 +80,7 @@ def convert_program(
7680
cc = self.build_conversion_context(
7781
parameters_mapping,
7882
node_formats,
83+
neutron_target_spec,
7984
conversion_config,
8085
custom_delegation_options,
8186
)
@@ -173,11 +178,12 @@ def map_inputs_to_parameters(edge_program: ExportedProgram) -> dict[str, Paramet
173178
def build_conversion_context(
174179
parameters_mapping: dict,
175180
node_formats: dict[Node, NodeFormat],
181+
neutron_target_spec: NeutronTargetSpec,
176182
conversion_config: ConversionConfig = _default_conversion_config,
177183
custom_delegation_options: CustomDelegationOptions = _default_delegation_options,
178184
) -> ConversionContext:
179185
tflite_builder = AtenModelBuilderDirector(
180-
3, "TFLite from EdgeProgram", conversion_config
186+
3, "TFLite from EdgeProgram", neutron_target_spec, conversion_config
181187
)
182188

183189
# Add "sentinel" buffer (defined in schema.fbs)

backends/nxp/backend/ir/converter/builder/model_builder.py

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
FlexTranspose,
4949
)
5050
from executorch.backends.nxp.backend.ir.tflite_optimizer import optimizer
51+
from executorch.backends.nxp.backend.neutron_target_spec import NeutronTargetSpec
5152

5253

5354
class ModelBuilder:
@@ -74,17 +75,21 @@ class ModelBuilder:
7475

7576
_zeros_tensor_map: Dict # Mapping 'string' shapes to 'tflT.Tensor' objects
7677

77-
_default_conversion_config = ConversionConfig()
78+
neutron_target_spec: NeutronTargetSpec
7879

7980
conversion_config: ConversionConfig
8081

82+
_default_conversion_config = ConversionConfig()
83+
8184
def __init__(
8285
self,
8386
model_version: int,
8487
model_description: str,
88+
neutron_target_spec: NeutronTargetSpec,
8589
conversion_config: ConversionConfig = _default_conversion_config,
8690
) -> None:
8791
self._tfl_model = tflite_model.Model(model_version, model_description)
92+
self.neutron_target_spec = neutron_target_spec
8893
self.conversion_config = conversion_config
8994

9095
self.op_code_type_index_map = {}
@@ -471,31 +476,7 @@ def finish(self) -> tflite_model.Model:
471476

472477
return self._tfl_model
473478

474-
def _assign_tensor_and_buffer_indices( # noqa C901
475-
self, allow_inputs_stripping: bool
476-
):
477-
"""Correctly initialize all references via indices in all tensors and buffers."""
478-
479-
# Assign each buffer its index
480-
for i, buffer in enumerate(self.get_buffers().vector):
481-
buffer.tmp_index = i
482-
483-
# Assign each tensor its index and its buffer index
484-
for i, tensor in enumerate(self.get_tensors().vector):
485-
if tensor.tmp_null_tensor:
486-
# Using -1 as the index to the 'tensors' vector is way of telling the TFLite inference engine, that
487-
# this tensor should not be used.
488-
# https://github.com/tensorflow/tensorflow/blob/05404d959119d41a8ffb8a75c6f232cfd8540d45/tensorflow/lite/kernels/kernel_util.cc#L79-L98
489-
tensor.tmp_index = -1
490-
else:
491-
tensor.tmp_index = i
492-
493-
tensor.buffer = tensor.tmp_buffer.tmp_index
494-
495-
# TODO Remove inputs and outputs that are not in the tensors collection
496-
497-
# Assign 'Outputs' and 'Inputs' their tensor indices
498-
outputs = self.get_sub_graph().outputs
479+
def _assign_io_tensor_indices(self, inputs, outputs, allow_inputs_stripping: bool):
499480
for tensor in outputs.tmp_outputs:
500481
try:
501482
outputs.append(tensor.tmp_index)
@@ -505,7 +486,6 @@ def _assign_tensor_and_buffer_indices( # noqa C901
505486
f"The tensor '{tensor.name}' is among the model outputs, but does NOT appear in the graph!",
506487
)
507488

508-
inputs = self.get_sub_graph().inputs
509489
for tensor in inputs.tmp_inputs:
510490
try:
511491
inputs.append(tensor.tmp_index)
@@ -520,14 +500,46 @@ def _assign_tensor_and_buffer_indices( # noqa C901
520500
f"The tensor '{tensor.name}' is among the model inputs, but does NOT appear in the graph!",
521501
)
522502

523-
# Assign each operator its inputs and outputs indices
524-
for operator in self.get_sub_graph().operators.vector:
503+
def _assign_operators_io_tensor_indices(self, operators):
504+
for operator in operators.vector:
525505
for inputTensor in operator.tmp_inputs:
526506
operator.inputs.append(inputTensor.tmp_index)
527507

528508
for outputTensor in operator.tmp_outputs:
529509
operator.outputs.append(outputTensor.tmp_index)
530510

511+
def _assign_tensor_and_buffer_indices(self, allow_inputs_stripping: bool):
512+
"""Correctly initialize all references via indices in all tensors and buffers."""
513+
514+
# Assign each buffer its index
515+
for i, buffer in enumerate(self.get_buffers().vector):
516+
buffer.tmp_index = i
517+
518+
# Assign each tensor its index and its buffer index
519+
for i, tensor in enumerate(self.get_tensors().vector):
520+
if tensor.tmp_null_tensor:
521+
# Using -1 as the index to the 'tensors' vector is way of telling the TFLite inference engine, that
522+
# this tensor should not be used.
523+
# https://github.com/tensorflow/tensorflow/blob/05404d959119d41a8ffb8a75c6f232cfd8540d45/tensorflow/lite/kernels/kernel_util.cc#L79-L98
524+
tensor.tmp_index = -1
525+
else:
526+
tensor.tmp_index = i
527+
528+
tensor.buffer = tensor.tmp_buffer.tmp_index
529+
530+
# TODO Remove inputs and outputs that are not in the tensors collection
531+
532+
subgraph = self.get_sub_graph()
533+
534+
# Assign 'Outputs' and 'Inputs' their tensor indices
535+
self._assign_io_tensor_indices(
536+
inputs=subgraph.inputs,
537+
outputs=subgraph.outputs,
538+
allow_inputs_stripping=allow_inputs_stripping,
539+
)
540+
# Assign each operator its inputs and outputs indices
541+
self._assign_operators_io_tensor_indices(operators=subgraph.operators)
542+
531543
def _build_operator_code(
532544
self, op_type: BuiltinOperator, version, custom_code: str = None
533545
):

0 commit comments

Comments
 (0)