Skip to content

Commit 10a6888

Browse files
authored
Merge branch 'main' into xnn_lock_init
2 parents 9527944 + cd0e584 commit 10a6888

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+555
-300
lines changed

.ci/scripts/utils.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ retry () {
1717
}
1818

1919
clean_executorch_install_folders() {
20-
./install_requirements.sh --clean
20+
./install_executorch.sh --clean
2121
}
2222

2323
install_executorch() {
2424
which pip
2525
# Install executorch, this assumes that Executorch is checked out in the
2626
# current directory.
2727
if [[ "${1:-}" == "use-pt-pinned-commit" ]]; then
28-
./install_requirements.sh --pybind xnnpack --use-pt-pinned-commit
28+
./install_executorch.sh --pybind xnnpack --use-pt-pinned-commit
2929
else
30-
./install_requirements.sh --pybind xnnpack
30+
./install_executorch.sh --pybind xnnpack
3131
fi
3232
# Just print out the list of packages for debugging
3333
pip list

.github/workflows/apple.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
paths:
1010
- .ci/scripts/setup-ios.sh
1111
- .github/workflows/apple.yml
12-
- install_requirements.sh
12+
- install_executorch.sh
1313
- backends/apple/**
1414
- build/build_apple_frameworks.sh
1515
- build/build_apple_llm_demo.sh

.github/workflows/pull.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ jobs:
200200
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "cmake"
201201
202202
# install pybind
203-
bash install_requirements.sh --pybind xnnpack
203+
bash install_executorch.sh --pybind xnnpack
204204
205205
# install Llava requirements
206206
bash examples/models/llama/install_requirements.sh
@@ -333,6 +333,9 @@ jobs:
333333

334334
unittest-arm:
335335
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
336+
permissions:
337+
id-token: write
338+
contents: read
336339
with:
337340
runner: linux.2xlarge
338341
docker-image: executorch-ubuntu-22.04-arm-sdk
@@ -433,7 +436,7 @@ jobs:
433436
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "cmake"
434437
435438
# install pybind
436-
bash install_requirements.sh --pybind xnnpack
439+
bash install_executorch.sh --pybind xnnpack
437440
438441
# install phi-3-mini requirements
439442
bash examples/models/phi-3-mini/install_requirements.sh
@@ -460,7 +463,7 @@ jobs:
460463
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "cmake"
461464
462465
# install pybind
463-
bash install_requirements.sh --pybind xnnpack
466+
bash install_executorch.sh --pybind xnnpack
464467
465468
# install llama requirements
466469
bash examples/models/llama/install_requirements.sh
@@ -487,7 +490,7 @@ jobs:
487490
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "cmake"
488491
489492
# install pybind
490-
bash install_requirements.sh --pybind xnnpack
493+
bash install_executorch.sh --pybind xnnpack
491494
492495
# install llama requirements
493496
bash examples/models/llama/install_requirements.sh
@@ -514,7 +517,7 @@ jobs:
514517
PYTHON_EXECUTABLE=python bash .ci/scripts/setup-linux.sh "cmake"
515518
516519
# install pybind
517-
bash install_requirements.sh --pybind xnnpack
520+
bash install_executorch.sh --pybind xnnpack
518521
519522
# install llama requirements
520523
bash examples/models/llama/install_requirements.sh

.github/workflows/trunk.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ jobs:
132132
test-arm-backend-delegation:
133133
name: test-arm-backend-delegation
134134
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
135+
permissions:
136+
id-token: write
137+
contents: read
135138
with:
136139
runner: linux.2xlarge
137140
docker-image: executorch-ubuntu-22.04-arm-sdk
@@ -159,6 +162,9 @@ jobs:
159162
test-arm-reference-delegation:
160163
name: test-arm-reference-delegation
161164
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
165+
permissions:
166+
id-token: write
167+
contents: read
162168
with:
163169
runner: linux.2xlarge
164170
docker-image: executorch-ubuntu-22.04-arm-sdk

backends/apple/mps/setup.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ I 00:00:00.122615 executorch:mps_executor_runner.mm:501] Model verified successf
9797
### [Optional] Run the generated model directly using pybind
9898
1. Make sure `pybind` MPS support was installed:
9999
```bash
100-
./install_requirements.sh --pybind mps
100+
./install_executorch.sh --pybind mps
101101
```
102102
2. Run the `mps_example` script to trace the model and run it directly from python:
103103
```bash

backends/arm/_passes/fuse_quantized_activation_pass.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ def _is_fuseable_quantized_activation(self, node: Node):
1919
is_fuseable = min_val == 0
2020

2121
is_quantized = len(node.users) == 1 and next(iter(node.users)).target == q_op
22-
if is_quantized:
22+
if is_fuseable and is_quantized:
2323
quant_node = next(iter(node.users))
2424
zp = quant_node.args[2]
2525
qmin = quant_node.args[3]
26-
27-
return is_fuseable and is_quantized and zp == qmin
26+
return zp == qmin
27+
else:
28+
return False
2829

2930
def _is_fuseable_input(self, node: Node):
3031
return (

backends/arm/test/misc/test_multiple_outputs.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
import unittest
88

9+
import pytest
910
import torch
10-
from executorch.backends.arm.test import common
11+
from executorch.backends.arm.test import common, conftest
1112
from executorch.backends.arm.test.tester.arm_tester import ArmTester
13+
from executorch.exir.backend.compile_spec_schema import CompileSpec
1214

1315

1416
class TestMultipleOutputs(unittest.TestCase):
@@ -51,3 +53,46 @@ def test_tosa_BI_pipeline(self):
5153
.to_executorch()
5254
.run_method_and_compare_outputs(inputs=inputs, qtol=1.0)
5355
)
56+
57+
def _test_ethosu_BI_pipeline(
58+
self,
59+
module: torch.nn.Module,
60+
test_data: tuple[torch.Tensor],
61+
compile_spec: CompileSpec,
62+
):
63+
tester = (
64+
ArmTester(
65+
module,
66+
example_inputs=test_data,
67+
compile_spec=compile_spec,
68+
)
69+
.quantize()
70+
.export()
71+
.to_edge_transform_and_lower()
72+
.to_executorch()
73+
.serialize()
74+
)
75+
if conftest.is_option_enabled("corstone_fvp"):
76+
tester.run_method_and_compare_outputs(qtol=1, inputs=test_data)
77+
78+
@pytest.mark.corstone_fvp
79+
def test_u85_BI(self):
80+
module = self.MultipleOutputsModule()
81+
test_data = module.get_inputs()
82+
self._test_ethosu_BI_pipeline(
83+
module,
84+
test_data,
85+
common.get_u85_compile_spec(),
86+
)
87+
88+
@pytest.mark.corstone_fvp
89+
@conftest.expectedFailureOnFVP
90+
# TODO MLETORCH-598
91+
def test_u55_BI(self):
92+
module = self.MultipleOutputsModule()
93+
test_data = module.get_inputs()
94+
self._test_ethosu_BI_pipeline(
95+
module,
96+
test_data,
97+
common.get_u55_compile_spec(),
98+
)

backends/arm/test/runner_utils.py

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -115,50 +115,53 @@ def _get_input_quantization_params(
115115
return quant_params
116116

117117

118-
def _get_output_node(program: ExportedProgram) -> Node:
118+
def _get_output_nodes(program: ExportedProgram) -> list[Node]:
119119
"""
120120
Get output node to this model.
121121
122122
Args:
123-
program (ExportedProgram): The program to get output node from.
123+
program (ExportedProgram): The program to get the output nodes from.
124124
Returns:
125-
The node that is the output of 'program'.
125+
The nodes that are the outputs of the 'program'.
126126
"""
127-
127+
output_nodes = []
128128
for node in program.graph.nodes:
129129
if node.op == "output":
130-
return node
131-
raise RuntimeError("No output node found.")
130+
for output in node.args[0]:
131+
output_nodes.append(output)
132+
if len(output_nodes) == 0:
133+
raise RuntimeError("No output nodes found.")
134+
else:
135+
return output_nodes
132136

133137

134138
def _get_output_quantization_params(
135-
program: ExportedProgram, output_node: Node
136-
) -> Optional[QuantizationParams]:
139+
output_nodes: list[Node],
140+
) -> List[QuantizationParams]:
137141
"""
138142
Get output QuantizationParams from a program.
139143
Args:
140-
program (ExportedProgram): The program to get output quantization parameters from.
144+
output_nodes (list(Node)): A list of output nodes to get output quantization parameters from.
141145
Returns:
142146
QuantizationParams: The found quantization parameters.
143147
Raises:
144148
RuntimeError if no output quantization parameters are found.
145149
"""
146-
147-
quant_params = None
148-
for node in program.graph.nodes:
149-
if (
150-
node.target == torch.ops.quantized_decomposed.dequantize_per_tensor.default
151-
and node == output_node.args[0][0]
152-
):
153-
quant_params = QuantizationParams(
154-
node_name=node.args[0].name,
155-
scale=node.args[1],
156-
zp=node.args[2],
157-
qmin=node.args[3],
158-
qmax=node.args[4],
159-
dtype=node.args[5],
150+
quant_params = []
151+
for node in output_nodes:
152+
if node.target == torch.ops.quantized_decomposed.dequantize_per_tensor.default:
153+
quant_params.append(
154+
QuantizationParams(
155+
node_name=node.args[0].name,
156+
scale=node.args[1],
157+
zp=node.args[2],
158+
qmin=node.args[3],
159+
qmax=node.args[4],
160+
dtype=node.args[5],
161+
)
160162
)
161-
break # break early, there's only one output node
163+
if len(quant_params) == 0:
164+
raise RuntimeError("No Quantization parameters not found in exported model.")
162165
return quant_params
163166

164167

@@ -211,7 +214,7 @@ def __init__(
211214
self.input_names: list[str] = None
212215
self.output_name: str = None
213216
self.qp_input: list[QuantizationParams] = None
214-
self.qp_output: QuantizationParams = None
217+
self.qp_output: list[QuantizationParams] = None
215218
self.timeout = 480
216219
self.target_board: str = None
217220

@@ -226,19 +229,17 @@ def init_run(
226229
):
227230

228231
self.input_names = _get_input_names(edge_program)
229-
self.output_node = _get_output_node(exported_program)
230-
self.output_name = self.output_node.name
232+
self.output_nodes = _get_output_nodes(exported_program)
233+
231234
self.is_quantized = is_quantized
232235
self.target_board = target_board
233236

234237
if is_quantized:
235238
self.qp_input = _get_input_quantization_params(exported_program)
236-
self.qp_output = _get_output_quantization_params(
237-
exported_program, self.output_node
238-
)
239+
self.qp_output = _get_output_quantization_params(self.output_nodes)
239240
else:
240241
self.qp_input = [None] * len(self.input_names)
241-
self.qp_output = None
242+
self.qp_output = [None] * len(self.output_nodes)
242243

243244
self._has_init_run = True
244245

@@ -265,7 +266,7 @@ def run_corstone(
265266
save_bytes(self.intermediate_path, data, False, input_name, quant_param)
266267

267268
out_path = os.path.join(self.intermediate_path, "out")
268-
out_path_with_suffix = out_path + "-0.bin"
269+
269270
input_paths = []
270271
for name in self.input_names:
271272
input_paths.append(
@@ -281,6 +282,7 @@ def run_corstone(
281282
), f"Did not find build arm_executor_runner in path {elf_path}, run setup_testing.sh?"
282283

283284
cmd_line = f"executor_runner -m {pte_path} -o {out_path}"
285+
284286
for input_path in input_paths:
285287
cmd_line += f" -i {input_path}"
286288

@@ -362,11 +364,14 @@ def run_corstone(
362364
raise RuntimeError(
363365
f"Corstone simulation failed:\ncmd: {command_args[self.target_board]}\n, log: \n {result_stdout}\n{result.stderr.decode()}"
364366
)
365-
366-
tosa_ref_output = np.fromfile(out_path_with_suffix, dtype=np.float32)
367-
output_shape = self.output_node.args[0][0].meta["val"].shape
368-
tosa_ref_output = torch.from_numpy(tosa_ref_output).reshape(output_shape)
369-
return tosa_ref_output
367+
output_np = []
368+
for i, node in enumerate(self.output_nodes):
369+
tosa_ref_output = np.fromfile(
370+
os.path.join(self.intermediate_path, f"out-{i}.bin"), dtype=np.float32
371+
)
372+
output_shape = node.meta["val"].shape
373+
output_np.append(torch.from_numpy(tosa_ref_output).reshape(output_shape))
374+
return tuple(output_np)
370375

371376
def run_tosa_graph(
372377
self, graph: TosaGraph, inputs: list[np.ndarray] | list[torch.Tensor]

backends/arm/test/tester/analyze_output_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2024 Arm Limited and/or its affiliates.
1+
# Copyright 2024-2025 Arm Limited and/or its affiliates.
22
#
33
# This source code is licensed under the BSD-style license found in the
44
# LICENSE file in the root directory of this source tree.
@@ -9,7 +9,7 @@
99
import torch
1010
from executorch.backends.arm.test.runner_utils import (
1111
_get_input_quantization_params,
12-
_get_output_node,
12+
_get_output_nodes,
1313
_get_output_quantization_params,
1414
)
1515

@@ -228,9 +228,9 @@ def dump_error_output(
228228
export_stage = tester.stages.get(tester.stage_name(Export), None)
229229
quantize_stage = tester.stages.get(tester.stage_name(Quantize), None)
230230
if export_stage is not None and quantize_stage is not None:
231-
output_node = _get_output_node(export_stage.artifact)
231+
output_nodes = _get_output_nodes(export_stage.artifact)
232232
qp_input = _get_input_quantization_params(export_stage.artifact)
233-
qp_output = _get_output_quantization_params(export_stage.artifact, output_node)
233+
qp_output = _get_output_quantization_params(output_nodes)
234234
logger.error(f"Input QuantArgs: {qp_input}")
235235
logger.error(f"Output QuantArgs: {qp_output}")
236236

0 commit comments

Comments
 (0)