Skip to content

Commit 8349b47

Browse files
authored
Merge branch 'main' into toupstream/dim_pass
2 parents 00b4cbe + 22867a9 commit 8349b47

106 files changed

Lines changed: 5321 additions & 248 deletions

File tree

Some content is hidden

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

.ci/scripts/build-qnn-sdk.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ set_up_aot() {
4040
-DEXECUTORCH_BUILD_EXTENSION_FLAT_TENSOR=ON \
4141
-DEXECUTORCH_BUILD_EXTENSION_NAMED_DATA_MAP=ON \
4242
-DEXECUTORCH_BUILD_EXTENSION_TENSOR=ON \
43+
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON \
44+
-DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON \
4345
-DEXECUTORCH_ENABLE_EVENT_TRACER=ON \
4446
-DPYTHON_EXECUTABLE=python3
4547
cmake --build $PWD --target "PyQnnManagerAdaptor" -j$(nproc)

.github/workflows/android-release-artifacts.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ jobs:
5353
run: |
5454
VERSION="${{ inputs.version }}"
5555
FLAVOR="${{ inputs.flavor }}"
56+
if [ -z "$FLAVOR" ]; then
57+
FLAVOR="xnnpack"
58+
fi
5659
if [ -z "$VERSION" ]; then
5760
echo "No version name specified. Will create a snapshot AAR"
5861
echo "should-skip=false" >> $GITHUB_OUTPUT
@@ -115,9 +118,10 @@ jobs:
115118
fi
116119
117120
FLAVOR="${{ inputs.flavor }}"
118-
if [ ! -z "$FLAVOR" ]; then
119-
GRADLE_ARGS+=" -Dflavor=${FLAVOR}"
121+
if [ -z "$FLAVOR" ]; then
122+
FLAVOR="xnnpack"
120123
fi
124+
GRADLE_ARGS+=" -Dflavor=${FLAVOR}"
121125
122126
if [[ "$FLAVOR" == "vulkan" || "$FLAVOR" == "all" || -z "$FLAVOR" ]]; then
123127
curl -O https://sdk.lunarg.com/sdk/download/1.4.321.1/linux/vulkansdk-linux-x86_64-1.4.321.1.tar.xz

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ cmake-out*
1818
cmake-out-android/
1919
build-android/
2020
build-x86/
21+
build-hexagon/
2122
dist/
2223
arm-scratch/
2324
executorch.egg-info

CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ project(executorch)
5050

5151
set(EXECUTORCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
5252

53+
# Hexagon toolchain with release build complains about code in third party
54+
# libraries.
55+
if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "Hexagon" AND "${CMAKE_BUILD_TYPE}"
56+
STREQUAL "Release"
57+
)
58+
add_compile_options(
59+
-Wno-error=format -Wno-error=implicit-int-conversion
60+
-Wno-error=unused-variable -Wno-error=unused-function
61+
)
62+
endif()
63+
5364
# --- ExecuTorch Version ---
5465
# Parse version from version.txt (single source of truth)
5566
file(READ "${EXECUTORCH_ROOT}/version.txt" ET_VERSION_STRING)

backends/arm/_passes/insert_table_ops.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class TableOps:
3939
exir_ops.edge.aten.floor.default: torch.floor,
4040
exir_ops.edge.aten.log.default: torch.log,
4141
exir_ops.edge.aten.log1p.default: torch.log1p,
42+
exir_ops.edge.aten.log10.default: torch.log10,
4243
exir_ops.edge.aten.reciprocal.default: torch.reciprocal,
4344
exir_ops.edge.aten.rsqrt.default: torch.rsqrt,
4445
exir_ops.edge.aten.sigmoid.default: torch.sigmoid,

backends/arm/_passes/rewrite_conv_pass.py

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ def _add_bias(
167167
weight_node: torch.fx.Node,
168168
) -> torch.fx.Node:
169169
output_channels = get_first_fake_tensor(node).shape[1]
170-
# add a node containging zeros if quantized, use int32, otherwise use float32
171-
if "output_qparams" in node.meta and len(node.meta["output_qparams"]) > 0:
170+
# add a node containing zeros if quantized, use int32, otherwise use float32
171+
if self._is_quantized_conv(node):
172172
bias_data = torch.zeros(size=(output_channels,), dtype=torch.int32)
173173
else:
174174
output_dtype = node.meta["val"].dtype
@@ -188,9 +188,40 @@ def _add_bias(
188188
node.update_arg(2, bias_node)
189189
return bias_node
190190

191-
def insert_output_rescale(self, graph_module, node):
192-
input_qparams = get_input_qparams(node)
193-
output_qparams = get_output_qparams(node)[0]
191+
def _is_quantized_conv(self, node: torch.fx.Node) -> bool:
192+
return bool(node.meta.get("input_qparams", {}))
193+
194+
def _get_effective_output_qparams(self, node: torch.fx.Node):
195+
"""Return the quantized output domain for a conv node.
196+
197+
Quantization annotation may place output qparams on a following
198+
activation instead of on the conv itself. If that activation is not
199+
fuseable, it survives as a quantized ``clamp`` and still owns the
200+
branch output qparams needed for the conv output rescale.
201+
202+
"""
203+
output_qparams = node.meta.get("output_qparams", {})
204+
if output_qparams:
205+
return output_qparams
206+
207+
users = list(node.users)
208+
if len(users) != 1:
209+
raise ValueError(
210+
f"RewriteConvPass: No output quantization parameter found in node {node}\n"
211+
f"original_aten={node.meta.get('original_aten', 'None')}"
212+
)
213+
214+
activation = users[0]
215+
if activation.target == exir_ops.edge.aten.clamp.default:
216+
activation_output_qparams = activation.meta.get("output_qparams", {})
217+
if activation_output_qparams:
218+
return activation_output_qparams
219+
220+
return get_output_qparams(node)
221+
222+
def insert_output_rescale(self, graph_module, source_node, conv_node):
223+
input_qparams = get_input_qparams(source_node)
224+
output_qparams = self._get_effective_output_qparams(source_node)[0]
194225
weight_qparams = input_qparams[1]
195226
input_qparams = input_qparams[0]
196227
is_per_channel = weight_qparams.per_channel
@@ -207,18 +238,18 @@ def insert_output_rescale(self, graph_module, node):
207238
itertools.cycle([output_qparams.get_scale_per_tensor()]),
208239
)
209240
]
210-
with graph_module.graph.inserting_after(node):
241+
with graph_module.graph.inserting_after(conv_node):
211242
rescale_node = create_node(
212243
graph=graph_module.graph,
213244
op_target=exir_ops.backend.tosa.RESCALE.default,
214245
args=(
215-
node,
246+
conv_node,
216247
output_qparams.dtype,
217248
post_conv2d_scale,
218249
0,
219250
output_qparams.get_zp_per_tensor(),
220251
),
221-
from_node=node,
252+
from_node=source_node,
222253
)
223254
return rescale_node
224255

@@ -347,15 +378,17 @@ def call(self, graph_module: torch.fx.GraphModule) -> PassResult: # noqa: C901
347378
tosa_node_fake_tensor.dtype == torch.int32
348379
and input_fake_tensor.dtype == torch.int8
349380
):
350-
output_rescale = self.insert_output_rescale(graph_module, tosa_op)
381+
output_rescale = self.insert_output_rescale(graph_module, node, tosa_op)
351382
node.replace_all_uses_with(output_rescale)
352383
elif (
353384
tosa_node_fake_tensor.dtype == torch.int32
354385
and input_fake_tensor.dtype == torch.int16
355386
):
356387
has_bias = len(node.meta["input_qparams"]) > 2
357388
if not has_bias:
358-
output_rescale = self.insert_output_rescale(graph_module, tosa_op)
389+
output_rescale = self.insert_output_rescale(
390+
graph_module, node, tosa_op
391+
)
359392
node.replace_all_uses_with(output_rescale)
360393
else:
361394
node.replace_all_uses_with(tosa_op)

backends/arm/operator_support/tosa_profile_supported_op_lists.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
exir_ops.edge.aten.expm1.default,
5757
exir_ops.edge.aten.log.default,
5858
exir_ops.edge.aten.log1p.default,
59+
exir_ops.edge.aten.log10.default,
5960
exir_ops.edge.aten.linear.default,
6061
exir_ops.edge.aten.split_with_sizes_copy.default,
6162
exir_ops.edge.aten.split_copy.Tensor,

backends/arm/quantizer/quantization_annotator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ def _match_pattern(
502502
torch.ops.aten.sinh.default,
503503
torch.ops.aten.atan.default,
504504
torch.ops.aten.log1p.default,
505+
torch.ops.aten.log10.default,
505506
torch.ops.aten.acosh.default,
506507
torch.ops.aten.sign.default,
507508
torch.ops.aten.asinh.default,

backends/arm/runtime/VGFSetup.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ VkResult allocate_tensor(
173173
.memoryTypeIndex = memory_index,
174174
};
175175

176-
vkAllocateMemory(device, &allocate_info, nullptr, memory);
176+
result = vkAllocateMemory(device, &allocate_info, nullptr, memory);
177+
if (result != VK_SUCCESS) {
178+
ET_LOG(Error, "Failed to allocate tensor memory, error %d", result);
179+
vkDestroyTensorARM(device, *tensor, nullptr);
180+
return result;
181+
}
177182

178183
// Bind tensor to memory
179184
const VkBindTensorMemoryInfoARM bind_info = {
@@ -183,7 +188,13 @@ VkResult allocate_tensor(
183188
.memory = *memory,
184189
.memoryOffset = 0,
185190
};
186-
vkBindTensorMemoryARM(device, 1, &bind_info);
191+
result = vkBindTensorMemoryARM(device, 1, &bind_info);
192+
if (result != VK_SUCCESS) {
193+
ET_LOG(Error, "Failed to bind tensor memory, error %d", result);
194+
vkDestroyTensorARM(device, *tensor, nullptr);
195+
vkFreeMemory(device, *memory, nullptr);
196+
return result;
197+
}
187198

188199
VkTensorViewCreateInfoARM tensor_view_info = {
189200
.sType = VK_STRUCTURE_TYPE_TENSOR_VIEW_CREATE_INFO_ARM,
@@ -799,7 +810,10 @@ bool VgfRepr::process_vgf(const char* vgf_data, ArrayRef<CompileSpec> specs) {
799810
.bindPoint = bind_point_requirement.bindPoint,
800811
.objectIndex = 0, // NOTE: tied to numObjects assert above
801812
};
802-
VkMemoryRequirements2 memory_requirements;
813+
VkMemoryRequirements2 memory_requirements = {
814+
.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2,
815+
.pNext = nullptr,
816+
};
803817
vkGetDataGraphPipelineSessionMemoryRequirementsARM(
804818
vk_device, &memory_requirements_info, &memory_requirements);
805819

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
# All rights reserved.
3+
# Copyright 2024-2026 Arm Limited and/or its affiliates.
4+
#
5+
# This source code is licensed under the BSD-style license found in the
6+
# LICENSE file in the root directory of this source tree.
7+
8+
9+
from typing import Tuple
10+
11+
import torch
12+
from executorch.backends.arm.test import common
13+
14+
from executorch.backends.arm.test.tester.test_pipeline import (
15+
EthosU55PipelineINT,
16+
EthosU85PipelineINT,
17+
TosaPipelineINT,
18+
VgfPipeline,
19+
)
20+
21+
aten_op = "torch.ops.aten.log10.default"
22+
exir_op = "executorch_exir_dialects_edge__ops_aten_log10_default"
23+
24+
input_t1 = Tuple[torch.Tensor]
25+
26+
27+
def _tensor(values):
28+
return torch.tensor(values, dtype=torch.float32)
29+
30+
31+
test_data_suite = {
32+
# (test_name, test_data)
33+
"tiny_positive": lambda: (_tensor([5e-4, 8e-4, 9e-4, 1e-3, 1.2e-3])),
34+
"mixed_range": lambda: (_tensor([1e-4, 5e-4, 2e-3, 1e-2, 5e-2])),
35+
"ones_rank4": lambda: (torch.ones(1, 10, 10, 10)),
36+
"ones_rank3": lambda: (torch.ones(10, 10, 10)),
37+
"rand": lambda: (torch.rand(10, 10) + 0.001),
38+
"randn_pos": lambda: (torch.randn(10) + 10),
39+
"randn_spread": lambda: (torch.max(torch.Tensor([0.1]), torch.randn(10) * 100)),
40+
"ramp": lambda: (torch.arange(0.01, 20, 0.2)),
41+
}
42+
43+
44+
class Log10(torch.nn.Module):
45+
def forward(self, x: torch.Tensor) -> torch.Tensor:
46+
return torch.log10(x)
47+
48+
49+
@common.parametrize("test_data", test_data_suite)
50+
def test_log10_tosa_INT(test_data: input_t1):
51+
pipeline = TosaPipelineINT[input_t1](Log10(), (test_data(),), aten_op, exir_op)
52+
pipeline.run()
53+
54+
55+
@common.parametrize("test_data", test_data_suite)
56+
@common.XfailIfNoCorstone300
57+
def test_log10_u55_INT(test_data: input_t1):
58+
EthosU55PipelineINT[input_t1](
59+
Log10(),
60+
(test_data(),),
61+
aten_op,
62+
exir_op,
63+
).run()
64+
65+
66+
@common.parametrize("test_data", test_data_suite)
67+
@common.XfailIfNoCorstone320
68+
def test_log10_u85_INT(test_data: input_t1):
69+
EthosU85PipelineINT[input_t1](
70+
Log10(),
71+
(test_data(),),
72+
aten_op,
73+
exir_op,
74+
).run()
75+
76+
77+
@common.parametrize("test_data", test_data_suite)
78+
@common.SkipIfNoModelConverter
79+
def test_log10_vgf_quant(test_data: input_t1):
80+
pipeline = VgfPipeline[input_t1](
81+
Log10(),
82+
(test_data(),),
83+
aten_op,
84+
exir_op,
85+
quantize=True,
86+
)
87+
pipeline.run()

0 commit comments

Comments
 (0)