Skip to content

Commit 5f0000e

Browse files
authored
Merge branch 'main' into addFake
2 parents 5bfb411 + dd9a85a commit 5f0000e

File tree

61 files changed

+901
-552
lines changed

Some content is hidden

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

61 files changed

+901
-552
lines changed

.ci/scripts/unittest-macos.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ if [[ "$BUILD_TOOL" == "cmake" ]]; then
3535
.ci/scripts/unittest-macos-cmake.sh
3636
elif [[ "$BUILD_TOOL" == "buck2" ]]; then
3737
.ci/scripts/unittest-buck2.sh
38-
.ci/scripts/unittest-macos-buck2.sh
38+
# .ci/scripts/unittest-macos-buck2.sh
3939
else
4040
echo "Unknown build tool $BUILD_TOOL"
4141
exit 1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ cmake-android-out/
1515
cmake-ios-out/
1616
cmake-out*
1717
cmake-out-android/
18+
dist/
1819
ethos-u-scratch/
1920
executorch.egg-info
2021
pip-out/

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,10 @@ if(EXECUTORCH_BUILD_EXECUTOR_RUNNER)
922922
endif()
923923
endif()
924924

925+
if(EXECUTORCH_BUILD_COREML)
926+
list(APPEND _executor_runner_libs coremldelegate)
927+
endif()
928+
925929
add_executable(executor_runner ${_executor_runner__srcs})
926930
if(CMAKE_BUILD_TYPE STREQUAL "Release")
927931
if(APPLE)

backends/apple/coreml/runtime/delegate/ETCoreMLModelManager.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ __attribute__((objc_subclassing_restricted))
7171
/// @param error On failure, error is filled with the failure information.
7272
/// @retval `YES` if the execution succeeded otherwise `NO`.
7373
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
74-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
74+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
7575
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
7676
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
7777
error:(NSError* __autoreleasing*)error;

backends/apple/coreml/runtime/delegate/ETCoreMLModelManager.mm

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ - (BOOL)executeModelWithHandle:(ModelHandle *)handle
734734
}
735735

736736
- (BOOL)executeModelWithHandle:(ModelHandle *)handle
737-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
737+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
738738
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
739739
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
740740
error:(NSError * __autoreleasing *)error {
@@ -785,6 +785,12 @@ - (BOOL)executeModelWithHandle:(ModelHandle *)handle
785785
return NO;
786786
}
787787

788+
// Resize for dynamic shapes
789+
for (int i = 0; i < outputArgs.size(); i++) {
790+
auto new_size = to_vector<size_t>(modelOutputs[i].shape);
791+
outputArgs[i].resize(new_size);
792+
argsVec[model.orderedInputNames.count + i].resize(new_size);
793+
}
788794
::set_outputs(outputArgs, modelOutputs);
789795
return YES;
790796
}

backends/apple/coreml/runtime/delegate/backend_delegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class BackendDelegate {
8989
/// @param error On failure, error is filled with the failure information.
9090
/// @retval `true` if the execution succeeded otherwise `false`.
9191
virtual bool execute(Handle* handle,
92-
const std::vector<MultiArray>& args,
92+
std::vector<MultiArray>& args,
9393
const ModelLoggingOptions& logging_options,
9494
ModelEventLogger* event_logger,
9595
std::error_code& error) const noexcept = 0;

backends/apple/coreml/runtime/delegate/backend_delegate.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ - (ModelHandle*)loadModelFromAOTData:(NSData*)data
104104
error:(NSError* __autoreleasing*)error;
105105

106106
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
107-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
107+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
108108
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
109109
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
110110
error:(NSError* __autoreleasing*)error;
@@ -199,7 +199,7 @@ - (ModelHandle*)loadModelFromAOTData:(NSData*)data
199199
}
200200

201201
- (BOOL)executeModelWithHandle:(ModelHandle*)handle
202-
argsVec:(const std::vector<executorchcoreml::MultiArray>&)argsVec
202+
argsVec:(std::vector<executorchcoreml::MultiArray>&)argsVec
203203
loggingOptions:(const executorchcoreml::ModelLoggingOptions&)loggingOptions
204204
eventLogger:(const executorchcoreml::ModelEventLogger* _Nullable)eventLogger
205205
error:(NSError* __autoreleasing*)error {
@@ -286,7 +286,7 @@ explicit BackendDelegateImpl(const Config& config) noexcept
286286
}
287287

288288
bool execute(Handle* handle,
289-
const std::vector<MultiArray>& args,
289+
std::vector<MultiArray>& args,
290290
const ModelLoggingOptions& logging_options,
291291
ModelEventLogger *event_logger,
292292
std::error_code& ec) const noexcept override {

backends/apple/coreml/runtime/delegate/coreml_backend_delegate.mm

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@
1212
#import <coreml_backend/delegate.h>
1313
#import <executorch/runtime/core/evalue.h>
1414
#import <executorch/runtime/platform/log.h>
15+
#import <executorch/runtime/kernel/kernel_includes.h>
1516
#import <memory>
1617
#import <model_event_logger.h>
1718
#import <model_logging_options.h>
1819
#import <multiarray.h>
1920
#import <objc_safe_cast.h>
2021
#import <unordered_map>
2122
#import <vector>
23+
#include <array>
2224

2325
#ifdef ET_EVENT_TRACER_ENABLED
2426
#import <model_event_logger_impl.h>
@@ -40,6 +42,9 @@
4042
using executorch::runtime::FreeableBuffer;
4143
using executorch::runtime::get_backend_class;
4244
using executorch::runtime::Result;
45+
using executorch::aten::SizesType;
46+
using executorch::aten::Tensor;
47+
using executorch::runtime::kTensorDimensionLimit;
4348

4449
std::optional<MultiArray::DataType> get_data_type(ScalarType scalar_type) {
4550
switch (scalar_type) {
@@ -221,6 +226,21 @@ ModelLoggingOptions get_logging_options(BackendExecutionContext& context) {
221226
ETCoreMLStrings.delegateIdentifier.UTF8String);
222227
#endif
223228

229+
// Resize for dynamic shape
230+
std::array<SizesType, kTensorDimensionLimit> new_shape;
231+
for (size_t i = nInputs; i < nInputs + nOutputs; i++) {
232+
Tensor& t = args[i]->toTensor();
233+
int rank = delegate_args[i].layout().rank();
234+
assert (rank <= new_shape.size());
235+
for (int d = 0; d < rank; d++) {
236+
new_shape[d] = delegate_args[i].layout().shape()[d];
237+
}
238+
ET_CHECK_OR_RETURN_ERROR(
239+
resize_tensor(t, ArrayRef(new_shape.data(), rank)) == Error::Ok,
240+
DelegateInvalidHandle,
241+
"%s: Failed to resize delegate output %zu", ETCoreMLStrings.delegateIdentifier.UTF8String, i);
242+
}
243+
224244
return Error::Ok;
225245
}
226246

backends/apple/coreml/runtime/delegate/multiarray.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class MultiArray final {
8484
/// Returns `true` if the memory layout is packed otherwise `false`.
8585
bool is_packed() const noexcept;
8686

87+
// Resizes memory layout
88+
// New shape must be the same dimension and no larger than current shape in all dimensions
89+
// New format is contiguous
90+
void resize(const std::vector<size_t>& shape);
91+
8792
private:
8893
DataType dataType_;
8994
std::vector<size_t> shape_;
@@ -126,6 +131,8 @@ class MultiArray final {
126131
*ptr = value;
127132
}
128133

134+
void resize(const std::vector<size_t>& shape) { layout_.resize(shape); }
135+
129136
private:
130137
void* data(const std::vector<size_t>& indices) const noexcept;
131138

backends/apple/coreml/runtime/delegate/multiarray.mm

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,24 @@ ssize_t get_data_offset(size_t index, const std::vector<size_t>& shape, const st
512512

513513
namespace executorchcoreml {
514514

515+
void MultiArray::MemoryLayout::resize(const std::vector<size_t>& shape) {
516+
assert(shape.size() == shape_.size());
517+
for (int i = 0; i < shape.size(); ++i) {
518+
assert (shape[i] >= 1);
519+
assert(shape[i] <= shape_[i]);
520+
}
521+
int stride = 1;
522+
for (int i = shape.size() - 1; i >= 0; --i) {
523+
shape_[i] = shape[i];
524+
strides_[i] = stride;
525+
if (shape[i] > 1) {
526+
stride *= shape[i];
527+
}
528+
}
529+
}
530+
531+
532+
515533
size_t MultiArray::MemoryLayout::num_elements() const noexcept {
516534
if (shape_.size() == 0) {
517535
return 0;

backends/apple/coreml/runtime/test/BackendDelegateTests.mm

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,9 @@ - (void)testAddModelExecution {
162162
MLMultiArray *output = [ETCoreMLTestUtils filledMultiArrayWithShape:inputs[0].shape dataType:inputs[0].dataType repeatedValue:@(0) error:&localError];
163163
NSArray<MLMultiArray *> *args = [inputs arrayByAddingObject:output];
164164
std::error_code errorCode;
165+
auto argsVec = to_multiarrays(args);
165166
XCTAssertTrue(_delegate->execute(handle,
166-
to_multiarrays(args),
167+
argsVec,
167168
ModelLoggingOptions(),
168169
nullptr,
169170
errorCode));
@@ -187,8 +188,9 @@ - (void)testMulModelExecution {
187188
MLMultiArray *output = [ETCoreMLTestUtils filledMultiArrayWithShape:inputs[0].shape dataType:inputs[0].dataType repeatedValue:@(0) error:&localError];
188189
NSArray<MLMultiArray *> *args = [inputs arrayByAddingObject:output];
189190
std::error_code errorCode;
190-
XCTAssertTrue(_delegate->execute(handle,
191-
to_multiarrays(args),
191+
auto argsVec = to_multiarrays(args);
192+
XCTAssertTrue(_delegate->execute(handle,
193+
argsVec,
192194
ModelLoggingOptions(),
193195
nullptr,
194196
errorCode));

backends/apple/coreml/runtime/test/MultiArrayTests.mm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,20 @@ - (void)testNonAdjacentDataCopy {
130130
[self verifyDataCopyWithShape:shape srcStrides:srcStrides dstStrides:dstStrides];
131131
}
132132

133+
- (void)testResize {
134+
std::vector<size_t> shape = {3, 1, 2, 5};
135+
std::vector<ssize_t> strides = {1*2*5, 2*5, 5, 1};
136+
std::vector<uint8_t> storage;
137+
std::vector<size_t> newShape = {3, 1, 1, 1};
138+
139+
auto array = make_multi_array_and_fill<int>(shape, strides, storage);
140+
for (size_t i = 0; i < array.layout().rank(); ++i) {
141+
XCTAssertEqual(array.layout().shape()[i], shape[i]);
142+
}
143+
array.resize(newShape);
144+
for (size_t i = 0; i < array.layout().rank(); ++i) {
145+
XCTAssertEqual(array.layout().shape()[i], newShape[i]);
146+
}
147+
}
148+
133149
@end

backends/apple/coreml/runtime/workspace/executorchcoreml.xcodeproj/xcshareddata/xcschemes/executorchcoreml_tests.xcscheme

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
BlueprintName = "executorchcoreml_tests"
2424
ReferencedContainer = "container:executorchcoreml.xcodeproj">
2525
</BuildableReference>
26+
<SkippedTests>
27+
<Test
28+
Identifier = "ETCoreMLModelDebuggerTests/testMV3ProgramDebugging">
29+
</Test>
30+
</SkippedTests>
2631
</TestableReference>
2732
</Testables>
2833
</TestAction>

backends/arm/_passes/arm_pass_manager.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@
4444
from executorch.backends.arm._passes.decompose_select import ( # type: ignore[import-not-found]
4545
DecomposeSelectPass,
4646
)
47-
from executorch.backends.arm._passes.decompose_softmaxes_pass import (
48-
DecomposeSoftmaxesPass,
47+
from executorch.backends.arm._passes.decompose_softmax_pass import DecomposeSoftmaxPass
48+
from executorch.backends.arm._passes.decompose_softmax_unstable_pass import (
49+
DecomposeSoftmaxUnstablePass,
4950
)
5051
from executorch.backends.arm._passes.decompose_var_pass import DecomposeVarPass
5152
from executorch.backends.arm._passes.fold_qdq_with_annotated_qparams_pass import (
@@ -81,7 +82,7 @@
8182
from executorch.backends.arm._passes.unsqueeze_scalar_placeholders_pass import (
8283
UnsqueezeScalarPlaceholdersPass,
8384
)
84-
from executorch.backends.arm.tosa_specification import TosaSpecification
85+
from executorch.backends.arm.tosa_specification import Tosa_0_80, TosaSpecification
8586
from executorch.backends.transforms.fuse_view_copy import FuseViewCopyTransform
8687

8788
from executorch.backends.transforms.replace_scalar_with_tensor import (
@@ -155,7 +156,7 @@ def _tosa_080_MI_pipeline(self, exported_program: ExportedProgram) -> GraphModul
155156
self.add_pass(DecomposeMeanDimPass())
156157
self.add_pass(ConvertMeanDimToAveragePoolPass())
157158
self.add_pass(DecomposeDivPass())
158-
self.add_pass(DecomposeSoftmaxesPass())
159+
self.add_pass(DecomposeSoftmaxPass())
159160
self.add_pass(ConvertFullLikeToFullPass())
160161
self.add_pass(ConvertToClampPass())
161162
self.add_pass(ConvertMinMaxPass())
@@ -204,6 +205,12 @@ def transform_for_annotation_pipeline(self, graph_module: GraphModule):
204205
self.add_pass(DecomposeVarPass())
205206
self.add_pass(DecomposeMeanDimPass())
206207
self.add_pass(DecomposeDivPass())
207-
self.add_pass(DecomposeSoftmaxesPass())
208+
209+
if isinstance(self.tosa_spec, Tosa_0_80) and self.tosa_spec.is_U55_subset:
210+
# Numerically stable softmax uses amax which is not supported on Ethos-U55
211+
self.add_pass(DecomposeSoftmaxUnstablePass())
212+
else:
213+
self.add_pass(DecomposeSoftmaxPass())
214+
208215
self.add_pass(ConvertMinMaxPass())
209216
return self._transform(graph_module)
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
import torch
7+
from executorch.exir.dialects._ops import ops as exir_ops
8+
from executorch.exir.pass_base import ExportPass
9+
10+
# For BI case
11+
torch_softmax = (torch.ops.aten.softmax.int, torch.ops.aten.log_softmax.int)
12+
# For MI case
13+
edge_softmax = (
14+
exir_ops.edge.aten._softmax.default,
15+
exir_ops.edge.aten._log_softmax.default,
16+
)
17+
log_softmax = (torch.ops.aten.log_softmax.int, exir_ops.edge.aten._log_softmax.default)
18+
19+
20+
def _get_logsoftmax_ops(op) -> tuple:
21+
"""
22+
Returns the (log_op, sub_op, amax_op, expo_op, sum_op, reciprocal_op), where the ops depends on if
23+
the softmax op is an aten or edge op.
24+
"""
25+
if op in edge_softmax:
26+
return (
27+
exir_ops.edge.aten.log.default,
28+
exir_ops.edge.aten.sub.Tensor,
29+
exir_ops.edge.aten.amax.default,
30+
exir_ops.edge.aten.exp.default,
31+
exir_ops.edge.aten.sum.dim_IntList,
32+
exir_ops.edge.aten.reciprocal.default,
33+
exir_ops.edge.aten.mul.Tensor,
34+
)
35+
if op in torch_softmax:
36+
return (
37+
torch.ops.aten.log.default,
38+
torch.ops.aten.sub.Tensor,
39+
torch.ops.aten.amax.default,
40+
torch.ops.aten.exp.default,
41+
torch.ops.aten.sum.dim_IntList,
42+
torch.ops.aten.reciprocal.default,
43+
torch.ops.aten.mul.Tensor,
44+
)
45+
raise RuntimeError(f"Can't get logsoftmax decomposition ops for op {op}")
46+
47+
48+
class DecomposeSoftmaxPass(ExportPass):
49+
"""
50+
This pass decomposes log_softmax or softmax into more primitive ops.
51+
Example:
52+
%op1 = amax(x)
53+
%op2 = sub(x, %op1)
54+
%op3 = exp(%op2)
55+
%op4 = sum(%op3, dim)
56+
%op5 = reciprocal(%op4)
57+
%op6 = mul(%op3, %op5)
58+
(in logsoftmax case: %op7 = log(%op6))
59+
"""
60+
61+
def call_operator(self, op, args, kwargs, meta):
62+
if op not in torch_softmax + edge_softmax:
63+
return super().call_operator(op, args, kwargs, meta)
64+
log_op, sub_op, max_op, exp_op, sum_op, reciprocal_op, mul_op = (
65+
_get_logsoftmax_ops(op)
66+
)
67+
_input = args[0]
68+
dim = [args[1]]
69+
op1 = super().call_operator(max_op, (_input, dim, True), {}, meta)
70+
op2 = super().call_operator(sub_op, (_input, op1), {}, meta)
71+
op3 = super().call_operator(exp_op, (op2,), {}, meta)
72+
op4 = super().call_operator(sum_op, (op3, dim, True), {}, meta)
73+
op5 = super().call_operator(reciprocal_op, (op4,), {}, meta)
74+
op6 = super().call_operator(mul_op, (op3, op5), {}, meta)
75+
if op in log_softmax:
76+
op6 = super().call_operator(log_op, (op6,), {}, meta)
77+
return op6

backends/arm/_passes/decompose_softmaxes_pass.py renamed to backends/arm/_passes/decompose_softmax_unstable_pass.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Copyright 2024 Arm Limited and/or its affiliates.
2-
# All rights reserved.
1+
# Copyright 2024-2025 Arm Limited and/or its affiliates.
32
#
43
# This source code is licensed under the BSD-style license found in the
54
# LICENSE file in the root directory of this source tree.
@@ -46,7 +45,7 @@ def get_logsoftmax_ops(op) -> tuple:
4645
raise RuntimeError(f"Can't get softmax decomposition ops for op {op}")
4746

4847

49-
class DecomposeSoftmaxesPass(ExportPass):
48+
class DecomposeSoftmaxUnstablePass(ExportPass):
5049
"""
5150
This pass decomposes log softmax or softmax into more primitive ops.
5251

0 commit comments

Comments
 (0)