Skip to content

Commit f74bad3

Browse files
authored
Merge branch 'main' into fix-vulkan-subgroup-target-1_3
2 parents c6b587e + 7013c8d commit f74bad3

40 files changed

Lines changed: 1495 additions & 384 deletions

.github/workflows/doc-build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ jobs:
104104
repository: pytorch/executorch
105105
download-artifact: docs
106106
ref: gh-pages
107+
# Publish on the host so actions/checkout's persisted credentials are
108+
# available to git push.
109+
run-with-docker: false
107110
timeout: 90
108111
script: |
109112
set -euo pipefail

.github/workflows/pull.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,7 @@ jobs:
14271427
docker-image: ci-image:executorch-ubuntu-22.04-clang12
14281428
submodules: 'recursive'
14291429
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
1430-
timeout: 120
1430+
timeout: 150
14311431
script: |
14321432
set -eux
14331433

backends/nxp/backend/ir/converter/node_converter.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging
77
import operator
88
from abc import ABC, abstractmethod
9+
from math import prod
910
from typing import Callable
1011

1112
import torch
@@ -465,29 +466,30 @@ def uses_shape_broadcasting(node: Node) -> bool:
465466
)
466467

467468
@staticmethod
468-
def at_least_one_input_shape_matches_the_output_shape(node: Node) -> bool:
469-
"""Determine if given PyTorch fx Node uses at least one input shape broadcasting for it's input nodes or not.
469+
def inputs_satisfy_broadcast_condition(node: Node) -> bool:
470+
"""Determine if given PyTorch fx Node has inputs that satisfy broadcasting conditions for Neutron or not.
470471
471472
:param node: PyTorch fx Node with 'all_input_nodes' initialized.
472-
:return: True, if at least one input has the same shape as the output node.
473+
:return: True, if at least one input has the same number of elements as the output node.
473474
False otherwise.
474475
"""
475476

476477
if node.all_input_nodes is None:
477478
logger.e(
478479
logger.Code.INTERNAL_ERROR,
479-
"node_converter.at_least_one_input_shape_matches_the_output_shape(): 'all_input_nodes' are None!",
480+
"node_converter.inputs_satisfy_broadcast_condition(): 'all_input_nodes' are None!",
480481
)
481482

482483
if len(node.all_input_nodes) == 0:
483484
logger.e(
484485
logger.Code.INTERNAL_ERROR,
485-
"node_converter.at_least_one_input_shape_matches_the_output_shape(): Operator has no inputs!",
486+
"node_converter.inputs_satisfy_broadcast_condition(): Operator has no inputs!",
486487
)
487488

488489
output_shape = node.meta["val"].shape
490+
num_elements = prod(output_shape)
489491

490492
return any(
491-
input_tensor.meta["val"].shape == output_shape
493+
prod(input_tensor.meta["val"].shape) == num_elements
492494
for input_tensor in node.all_input_nodes
493495
)

backends/nxp/backend/ir/converter/node_converters/ops_converters/add_tensor_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def _is_supported_on_target(
2626
parameters_mapping: dict[str, Parameter],
2727
custom_delegation_options: CustomDelegationOptions,
2828
) -> bool:
29-
if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node):
29+
if not NodeConverter.inputs_satisfy_broadcast_condition(node):
3030
return False
3131

3232
supported_types = [torch.int8, torch.uint8]

backends/nxp/backend/ir/converter/node_converters/ops_converters/mul_tensor_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def _is_supported_on_target(
2626
parameters_mapping: dict[str, Parameter],
2727
custom_delegation_options: CustomDelegationOptions,
2828
) -> bool:
29-
if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node):
29+
if not NodeConverter.inputs_satisfy_broadcast_condition(node):
3030
return False
3131

3232
supported_types = [torch.int8, torch.uint8]

backends/nxp/backend/ir/converter/node_converters/ops_converters/prelu_converter.py

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
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.
55

6+
import torch
7+
68
from executorch.backends.nxp.backend.data_format import NXP_NODE_FORMAT
9+
from executorch.backends.nxp.backend.edge_helper import input_rank
710
from executorch.backends.nxp.backend.ir.converter.node_converter import (
811
CustomDelegationOptions,
912
NodeConverter,
@@ -24,38 +27,17 @@ def _is_supported_on_target(
2427
parameters_mapping: dict[str, Parameter],
2528
custom_delegation_options: CustomDelegationOptions,
2629
) -> bool:
27-
node_shape = node.meta["val"].shape
28-
rank = len(node_shape)
29-
30-
# According to Neutron spec., PReLU can be done only on 4D tensors
31-
if rank != 4:
32-
return False
33-
34-
ch_idx, h_idx, w_idx = PReLUConverter._get_channel_spatial_indices(node)
35-
# According to Neutron spec., size of channels must be divisible by num_macs.
36-
num_macs = neutron_target_spec.get_num_macs()
37-
if node_shape[ch_idx] % num_macs != 0:
30+
if not NodeConverter.inputs_satisfy_broadcast_condition(node):
3831
return False
3932

40-
# According to Neutron spec., height * width cannot be greater than a given constant.
41-
if node_shape[w_idx] * node_shape[h_idx] > 4096:
33+
supported_types = [torch.int8, torch.uint8]
34+
if not NodeConverter.uses_quantization_type_for_io(
35+
node, supported_types, [0, 1], [0]
36+
):
4237
return False
4338

4439
return True
4540

46-
@staticmethod
47-
def _get_channel_spatial_indices(node: Node):
48-
if node.meta[NXP_NODE_FORMAT].is_channels_first():
49-
ch_idx = 1
50-
h_idx = 2
51-
w_idx = 3
52-
else:
53-
ch_idx = 3
54-
h_idx = 1
55-
w_idx = 2
56-
57-
return ch_idx, h_idx, w_idx
58-
5941
@staticmethod
6042
def _is_supported_in_IR(
6143
node: Node,
@@ -65,6 +47,12 @@ def _is_supported_in_IR(
6547
if len(node.args) != 2:
6648
return False
6749

50+
if input_rank(node, 0) > 2 and tuple(
51+
node.all_input_nodes[1].meta["val"].shape
52+
) != (1,):
53+
if not node.meta[NXP_NODE_FORMAT].is_channels_first():
54+
# Should never happen.
55+
return False
6856
return True
6957

7058
def convert(self, node: Node):

backends/nxp/backend/ir/converter/node_converters/ops_converters/sub_tensor_converter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def _is_supported_on_target(
2626
parameters_mapping: dict[str, Parameter],
2727
custom_delegation_options: CustomDelegationOptions,
2828
) -> bool:
29-
if not NodeConverter.at_least_one_input_shape_matches_the_output_shape(node):
29+
if not NodeConverter.inputs_satisfy_broadcast_condition(node):
3030
return False
3131

3232
supported_types = [torch.int8, torch.uint8]

backends/nxp/backend/node_format_inference.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from executorch.backends.nxp.backend.data_format import DataFormat, NXP_NODE_FORMAT
1212
from executorch.backends.nxp.backend.edge_helper import (
13+
input_rank,
1314
is_channels_last_dim_order,
1415
try_get_arg,
1516
)
@@ -26,6 +27,7 @@
2627
MaxPool2DWithIndices,
2728
MeanDim,
2829
PermuteCopy,
30+
Prelu,
2931
QuantizePerTensor,
3032
SumDimIntList,
3133
UpsampleBilinear2D,
@@ -54,6 +56,11 @@ class NodeFormatInference:
5456
UpsampleNearest2D: {"inputs": [0]},
5557
}
5658

59+
ops_conditionally_with_channels_first_nodes = {
60+
Prelu: {"inputs": [0]},
61+
torch.ops.aten.prelu.default: {"inputs": [0]},
62+
}
63+
5764
# A set of Edge Aten ops, which have the ability to change the format (for example - input nodes
5865
# are channels first but output is formatless).
5966
ops_that_can_change_tensor_format = {
@@ -64,6 +71,8 @@ class NodeFormatInference:
6471
SumDimIntList,
6572
}
6673

74+
prelu_targets = [Prelu, torch.ops.aten.prelu.default]
75+
6776
_type_changed_during_last_run: bool
6877

6978
# Mapping between Node and its ancestors (inputs)
@@ -160,6 +169,13 @@ def _infer_format_of_nodes(self, node: Node):
160169
f"Node format inference for node type: {op_type} not found!"
161170
)
162171

172+
elif node.target in self.prelu_targets:
173+
num_parameters_shape = tuple(node.args[1].meta["val"].shape)
174+
if input_rank(node, 0) > 2 and num_parameters_shape != (1,):
175+
self._handle_node_which_uses_channels_first_format(node)
176+
else:
177+
self._handle_node_which_can_use_any_node_format(node)
178+
163179
elif node.op != "call_function" or (
164180
hasattr(node, "target") and node.target in self._known_targets
165181
):
@@ -248,10 +264,14 @@ def _handle_node_which_uses_channels_first_format(self, node: Node):
248264
Function for assigning format to nodes that require channels first input (Conv, MaxPool etc.)
249265
"""
250266
op_type = self._get_node_op_type(node)
267+
ops_using_channels_first_format = (
268+
self.ops_with_channels_first_nodes
269+
| self.ops_conditionally_with_channels_first_nodes
270+
)
251271

252272
for index, ancestor_node in enumerate(self._node_inputs[node]):
253273
# Go through input nodes and assign them correct format
254-
if index in self.ops_with_channels_first_nodes[op_type]["inputs"]:
274+
if index in ops_using_channels_first_format[op_type]["inputs"]:
255275
self._assign_format_to_node(ancestor_node, DataFormat.CHANNELS_FIRST)
256276

257277
# We need to propagate channels first format up to already visited nodes

backends/nxp/tests/ir/converter/node_converter/test_add_tensor_converter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def test__basic_nsys_inference_qat(self, mocker, request):
115115
[ModelInputSpec((1, 4, 8, 8)), ModelInputSpec((8, 8))],
116116
id="2 inputs 4D + 2D.",
117117
),
118+
pytest.param(
119+
[ModelInputSpec((10,)), ModelInputSpec((1, 1))],
120+
id="2 inputs 1D + 2D, num_elems of input == num_elems of output",
121+
),
118122
],
119123
)
120124
def test__broadcast(self, mocker, request, input_spec):

backends/nxp/tests/ir/converter/node_converter/test_mul_tensor_converter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ def test__basic_nsys_inference_qat(self, mocker, request, x_input_shape):
9696
pytest.param(
9797
[ModelInputSpec((4,)), ModelInputSpec((4, 4))], id="2 inputs 1D+2D."
9898
),
99+
pytest.param(
100+
[ModelInputSpec((10,)), ModelInputSpec((1, 1))],
101+
id="2 inputs 1D + 2D, num_elems of input == num_elems of output",
102+
),
99103
],
100104
)
101105
def test__broadcast(self, input_spec, mocker, request):

0 commit comments

Comments
 (0)