Skip to content

Commit 63481e3

Browse files
Jiseong-ohchong-chen01jingya-zhangxiongzhan-linghujonghun-cha
authored
Support more ops and verify more models (#14106)
Expands more ops and Verify more models ### Summary - Added for more OPs currently supported by Exynos - Added functionality for models for Showcase - Additional Module for Optimization - Fixed ci issue ### Test plan models="ic3, ic4, resnet18, resnet50, mv3, edsr, dl3, w2l, vit, mobilebert python -m executorch.examples.samsung.aot_compiler --model_name=$model -c E9955 cc @SS-JIA @digantdesai @kimishpatel --------- Signed-off-by: jiseong.oh <[email protected]> Co-authored-by: chong-chen <[email protected]> Co-authored-by: jingya-zhang <[email protected]> Co-authored-by: xz-linghu <[email protected]> Co-authored-by: Jonghun Cha <[email protected]>
1 parent 1d37845 commit 63481e3

Some content is hidden

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

64 files changed

+2970
-36
lines changed

.ci/scripts/setup-samsung-linux-deps.sh

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,6 @@ install_enn_backend() {
5454
rm -rf "${NDK_INSTALLATION_DIR}" && sudo mkdir -p "${NDK_INSTALLATION_DIR}"
5555
ANDROID_NDK_VERSION=r27b
5656

57-
pushd .
58-
cd /tmp
59-
curl -Os --retry 3 "https://ossci-android.s3.amazonaws.com/android-ndk-${ANDROID_NDK_VERSION}-linux.zip"
60-
unzip -qo "android-ndk-${ANDROID_NDK_VERSION}-linux.zip"
61-
62-
# Print the content for manual verification
63-
ls -lah "android-ndk-${ANDROID_NDK_VERSION}"
64-
sudo mv "android-ndk-${ANDROID_NDK_VERSION}"/* "${NDK_INSTALLATION_DIR}"
65-
popd
6657
# build Exynos backend
6758
export ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT:-/opt/ndk}
6859
bash backends/samsung/build.sh --build all

.github/workflows/pull.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ jobs:
901901
contents: read
902902
with:
903903
runner: linux.2xlarge
904-
docker-image: ci-image:executorch-ubuntu-22.04-gcc9
904+
docker-image: ci-image:executorch-ubuntu-22.04-clang12-android
905905
submodules: 'recursive'
906906
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
907907
timeout: 90
@@ -919,7 +919,7 @@ jobs:
919919
source .ci/scripts/setup-samsung-linux-deps.sh
920920
921921
# Test models serially
922-
models="mv2 ic3 resnet18 resnet50"
922+
models="mv2 ic3 resnet18 resnet50 mv3 ic4 dl3 edsr vit w2l"
923923
for model in $models; do
924924
python -m executorch.examples.samsung.aot_compiler --model_name=$model -c E9955
925925
done
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright (c) 2025 Samsung Electronics Co. LTD
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import torch
8+
from executorch.exir import ExportedProgram
9+
from executorch.exir.dialects._ops import ops as exir_ops
10+
from executorch.exir.pass_base import ExportPass, PassResult
11+
from torch._export.utils import get_param
12+
13+
14+
class Conv1dToConv2d(ExportPass):
15+
16+
def __init__(self, edge_program: ExportedProgram):
17+
super().__init__()
18+
self.edge_program = edge_program
19+
20+
def call(self, graph_module: torch.fx.GraphModule):
21+
graph = graph_module.graph
22+
node_list = list(graph.nodes)
23+
for node in node_list:
24+
if node.op == "call_function":
25+
if node.target == exir_ops.edge.aten.convolution.default:
26+
stride = list(node.args[3])
27+
if len(stride) != 1:
28+
continue
29+
30+
# convert 3dim weight to 4dim
31+
weight_node = node.args[1]
32+
weight_3dim = get_param(self.edge_program, weight_node)
33+
weight_4dim = torch.nn.Parameter(
34+
data=weight_3dim.data.contiguous().unsqueeze(dim=-1),
35+
requires_grad=False,
36+
)
37+
parameter_name = (
38+
self.edge_program.graph_signature.inputs_to_parameters[
39+
weight_node.name
40+
]
41+
)
42+
self.edge_program.state_dict[parameter_name] = weight_4dim
43+
weight_node.meta["val"] = weight_node.meta["val"].data.unsqueeze(
44+
dim=-1
45+
)
46+
47+
# Extend stride, padding, and dilation
48+
node.args = (
49+
node.args[0],
50+
node.args[1],
51+
node.args[2],
52+
node.args[3] + [1], # stride
53+
node.args[4] + [0], # padding
54+
node.args[5] + [1], # dilation
55+
node.args[6],
56+
node.args[7],
57+
node.args[8],
58+
)
59+
60+
# unsqueeze -> conv2d -> squeeze
61+
with graph.inserting_before(node):
62+
input_node = node.args[0]
63+
unsqueeze_before = graph.create_node(
64+
"call_function", exir_ops.edge.aten.unsqueeze_copy.default
65+
)
66+
unsqueeze_before.args = (
67+
input_node,
68+
-1,
69+
)
70+
node.replace_input_with(input_node, unsqueeze_before)
71+
72+
with graph.inserting_after(node):
73+
squeeze_after = graph.create_node(
74+
"call_function", exir_ops.edge.aten.squeeze_copy.dims
75+
)
76+
squeeze_after.args = (
77+
node,
78+
[-1],
79+
)
80+
original_users = [
81+
user for user in node.users if user != squeeze_after
82+
]
83+
for user in original_users:
84+
user.replace_input_with(node, squeeze_after)
85+
86+
graph_module.recompile()
87+
graph_module = super().call(graph_module).graph_module
88+
return PassResult(graph_module, True)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) 2025 Samsung Electronics Co. LTD
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
import executorch.exir.passes.constant_prop_pass as constant_prop_module
8+
from executorch.exir import ExportedProgram
9+
from executorch.exir.pass_base import ExportPass, PassResult
10+
from executorch.exir.passes.constant_prop_pass import constant_prop_pass
11+
from torch.fx import GraphModule
12+
13+
14+
class _constant_prop_context:
15+
def __init__(self):
16+
self.backup = constant_prop_module._DEFAULT_SKIP_TARGETS
17+
18+
def __enter__(self):
19+
constant_prop_module._DEFAULT_SKIP_TARGETS = (
20+
constant_prop_module._DEFAULT_SKIP_TARGETS_NO_QUANT
21+
)
22+
23+
def __exit__(self, exc_type, exc_val, exc_tb):
24+
constant_prop_module._DEFAULT_SKIP_TARGETS = self.backup
25+
26+
27+
class ConstantPropPass(ExportPass):
28+
"""
29+
Official constant_prop_pass will not fold Q-DQ
30+
But we need to fold quantized constant tensor as well as non-quantized one
31+
"""
32+
33+
def __init__(self, edge_program: ExportedProgram):
34+
super().__init__()
35+
self.edge_program = edge_program
36+
37+
def call(self, graph_module: GraphModule):
38+
with _constant_prop_context():
39+
_ = constant_prop_pass(self.edge_program)
40+
return PassResult(graph_module, True)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright (c) 2025 Samsung Electronics Co. LTD
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
from typing import Dict, Tuple
8+
9+
import torch
10+
from executorch.exir.dialects._ops import ops as exir_ops
11+
from executorch.exir.pass_base import ExportPass
12+
from torch._export.pass_base import Argument
13+
from torch._export.pass_infra.node_metadata import NodeMetadata
14+
from torch._export.pass_infra.proxy_value import ProxyValue
15+
16+
17+
class ReplaceOpsWithScalar(ExportPass):
18+
# Replace binary ops with scalar into binary ops with tensor.
19+
# Ops list below.
20+
_ops_with_scalar = {
21+
exir_ops.edge.aten.add.Scalar: exir_ops.edge.aten.add.Tensor,
22+
exir_ops.edge.aten.sub.Scalar: exir_ops.edge.aten.sub.Tensor,
23+
exir_ops.edge.aten.div.Scalar: exir_ops.edge.aten.div.Tensor,
24+
exir_ops.edge.aten.mul.Scalar: exir_ops.edge.aten.mul.Tensor,
25+
exir_ops.edge.aten.pow.Tensor_Scalar: exir_ops.edge.aten.pow.Tensor_Tensor,
26+
}
27+
28+
def __init__(self):
29+
super(ReplaceOpsWithScalar, self).__init__()
30+
31+
def call_operator(
32+
self,
33+
op,
34+
args: Tuple[Argument, ...],
35+
kwargs: Dict[str, Argument],
36+
meta: NodeMetadata,
37+
) -> ProxyValue:
38+
if op not in self._ops_with_scalar:
39+
return super().call_operator(op, args, kwargs, meta)
40+
41+
return super().call_operator(
42+
op=self._ops_with_scalar.get(op, op),
43+
args=(args[0], torch.tensor(args[1])),
44+
kwargs=kwargs,
45+
meta=meta,
46+
)

backends/samsung/builders/__init__.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,39 +9,83 @@
99
op_add,
1010
op_avg_pool2d,
1111
op_batch_norm,
12+
op_bmm,
1213
op_cat,
1314
op_clamp,
15+
op_constant_pad_nd,
1416
op_conv2d,
17+
op_div,
18+
op_embedding,
19+
op_expand_copy,
20+
op_gelu,
1521
op_getitem,
22+
op_hardswish,
1623
op_hardtanh,
24+
op_layer_norm,
25+
op_leaky_relu,
1726
op_linear,
27+
op_log_softmax,
1828
op_max_pool2d,
29+
op_maximum,
1930
op_mean_dim,
31+
op_minimum,
2032
op_mul,
2133
op_permute,
34+
op_pixel_shuffle,
2235
op_relu,
2336
op_reshape,
37+
op_rsqrt,
2438
op_select,
39+
op_slice_copy,
40+
op_softmax,
41+
op_sqrt,
42+
op_squeeze,
43+
op_sub,
44+
op_to_copy,
2545
op_unsqueeze,
46+
op_upsample_bilinear2d,
47+
op_upsample_nearest2d,
2648
)
2749

2850
__all__ = [
2951
node_visitor,
3052
op_add,
3153
op_avg_pool2d,
3254
op_batch_norm,
55+
op_bmm,
3356
op_cat,
3457
op_clamp,
3558
op_conv2d,
59+
op_constant_pad_nd,
60+
op_div,
61+
op_embedding,
62+
op_expand_copy,
63+
op_gelu,
3664
op_getitem,
65+
op_hardswish,
3766
op_hardtanh,
67+
op_layer_norm,
68+
op_leaky_relu,
3869
op_linear,
70+
op_log_softmax,
3971
op_max_pool2d,
72+
op_maximum,
4073
op_mean_dim,
74+
op_minimum,
4175
op_mul,
4276
op_permute,
77+
op_pixel_shuffle,
4378
op_relu,
4479
op_reshape,
80+
op_rsqrt,
4581
op_select,
82+
op_slice_copy,
83+
op_softmax,
84+
op_sqrt,
85+
op_squeeze,
86+
op_sub,
87+
op_to_copy,
4688
op_unsqueeze,
89+
op_upsample_bilinear2d,
90+
op_upsample_nearest2d,
4791
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright (c) 2025 Samsung Electronics Co. LTD
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
from typing import Dict
8+
9+
import torch
10+
from executorch.backends.samsung.builders.node_visitor import (
11+
NodeVisitor,
12+
register_node_visitor,
13+
)
14+
from executorch.backends.samsung.serialization.enn_graph_schema import EnnGraph
15+
16+
17+
@register_node_visitor
18+
class BMMVisitor(NodeVisitor):
19+
target = "aten.bmm.default"
20+
21+
def __init__(self, *args) -> None:
22+
super().__init__(*args)
23+
24+
def define_node(
25+
self,
26+
node: torch.fx.Node,
27+
enn_graph: EnnGraph,
28+
vals_to_ids: Dict[torch.Tensor, int],
29+
) -> None:
30+
input1 = node.args[0]
31+
input_id_1 = self.define_tensor(input1, enn_graph, vals_to_ids)
32+
input2 = node.args[1]
33+
input_id_2 = self.define_tensor(input2, enn_graph, vals_to_ids)
34+
35+
# output
36+
output_id = self.define_tensor(node, enn_graph, vals_to_ids)
37+
38+
enn_graph.define_op(
39+
node.name, "BATCH_MATMUL", [input_id_1, input_id_2], [output_id]
40+
)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Copyright (c) 2025 Samsung Electronics Co. LTD
2+
# All rights reserved
3+
#
4+
# This source code is licensed under the BSD-style license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
from typing import cast, Dict, List
8+
9+
import numpy as np
10+
11+
import torch
12+
from executorch.backends.samsung.builders.node_visitor import (
13+
NodeVisitor,
14+
register_node_visitor,
15+
)
16+
from executorch.backends.samsung.serialization.enn_graph_schema import EnnGraph
17+
from executorch.backends.transforms import get_shape
18+
19+
20+
@register_node_visitor
21+
class ConstantPadNDVisitor(NodeVisitor):
22+
target = "aten.constant_pad_nd.default"
23+
24+
def __init__(self, *args) -> None:
25+
super().__init__(*args)
26+
27+
def define_node(
28+
self,
29+
node: torch.fx.Node,
30+
enn_graph: EnnGraph,
31+
vals_to_ids: Dict[torch.Tensor, int],
32+
) -> None:
33+
input = node.args[0]
34+
input_id = self.define_tensor(input, enn_graph, vals_to_ids)
35+
36+
# torch padding order starts from the last axis, change the order to fit samsung lite-core
37+
paddings = np.reshape(cast(List[int], node.args[1]), (-1, 2))[::-1].astype(
38+
np.uint32
39+
)
40+
in_shape = get_shape(input)
41+
paddings = paddings.reshape(-1).tolist()
42+
paddings = [0] * (2 * len(in_shape) - len(paddings)) + paddings
43+
paddings = paddings[::2] + paddings[1::2]
44+
45+
padding_value = node.args[2]
46+
assert padding_value == 0.0, "Only Support pad constant 0 now."
47+
# output
48+
output_id = self.define_tensor(node, enn_graph, vals_to_ids)
49+
50+
params = {
51+
"explicit_padding": paddings,
52+
"padding": "EXPLICIT",
53+
"padding_type": "CONSTANT",
54+
}
55+
56+
enn_graph.define_op(node.name, "PAD", [input_id], [output_id], params)

0 commit comments

Comments
 (0)