diff --git a/.eslintrc.js b/.eslintrc.js index b09ff37d23..fd7eca7393 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,6 +8,7 @@ const VALID_CATEGORIES = [ 'Models - Image Embeddings', 'Models - Image Generation', 'Models - LLM', + 'Models - LLM Multimodal', 'Models - Object Detection', 'Models - Instance Segmentation', 'Models - Pose Estimation', diff --git a/apps/llm/components/llmModels.ts b/apps/llm/components/llmModels.ts index 1991578973..58b8c01d74 100644 --- a/apps/llm/components/llmModels.ts +++ b/apps/llm/components/llmModels.ts @@ -10,8 +10,11 @@ const llm = models.llm; export type LLMModelSources = LLMProps['model']; export const LLM_MODELS: ModelOption[] = [ - // Gemma4 - { label: 'Gemma4 E2B', value: llm.gemma4_e2b() }, + //Gemma 4 + { + label: 'Gemma 4 E2B', + value: llm.gemma4_e2b(), + }, // Llama 3.2 { label: 'Llama 3.2 1B', diff --git a/packages/react-native-executorch/android/libs/classes.jar b/packages/react-native-executorch/android/libs/classes.jar index be5ec2ee7f..81eee28f71 100644 Binary files a/packages/react-native-executorch/android/libs/classes.jar and b/packages/react-native-executorch/android/libs/classes.jar differ diff --git a/packages/react-native-executorch/common/runner/text_prefiller.cpp b/packages/react-native-executorch/common/runner/text_prefiller.cpp index 8325efae8f..370ca5c7f4 100644 --- a/packages/react-native-executorch/common/runner/text_prefiller.cpp +++ b/packages/react-native-executorch/common/runner/text_prefiller.cpp @@ -18,10 +18,11 @@ namespace llm { TextPrefiller::TextPrefiller(TextDecoderRunner *text_decoder_runner, bool use_kv_cache, bool enable_parallel_prefill, - int64_t max_seq_len) + int64_t max_seq_len, int32_t prefill_chunk_size) : text_decoder_runner_(text_decoder_runner), use_kv_cache_(use_kv_cache), enable_parallel_prefill_(enable_parallel_prefill), - max_seq_len_(max_seq_len > 0 ? max_seq_len : 128) {} + max_seq_len_(max_seq_len > 0 ? max_seq_len : 128), + prefill_chunk_size_(prefill_chunk_size) {} ::executorch::runtime::Result TextPrefiller::prefill(std::vector &prompt_tokens, @@ -31,11 +32,10 @@ TextPrefiller::prefill(std::vector &prompt_tokens, ET_CHECK_OK_OR_RETURN_ERROR(text_decoder_runner_->load()); } - // Check if we need to chunk the prompt tokens int32_t num_prompt_tokens = prompt_tokens.size(); - const int32_t chunk_size = static_cast(max_seq_len_); + int32_t chunk_size = + prefill_chunk_size_ > 0 ? prefill_chunk_size_ : max_seq_len_; - // If prompt tokens exceed chunk_size, we need to chunk them if (num_prompt_tokens > chunk_size) { uint64_t cur_token = 0; int num_tokens_to_process = 0; diff --git a/packages/react-native-executorch/common/runner/text_prefiller.h b/packages/react-native-executorch/common/runner/text_prefiller.h index 7929fe9c7f..b8cdb1b98c 100644 --- a/packages/react-native-executorch/common/runner/text_prefiller.h +++ b/packages/react-native-executorch/common/runner/text_prefiller.h @@ -19,8 +19,14 @@ namespace llm { class TextPrefiller { public: + // prefill_chunk_size: when > 0, the prompt is always processed in steps of + // this size (see prefill()). Set to the model's forward sequence-length cap + // for the MLX backend (its forward is exported with a sliding-window bound + // and one-shot prefill spikes Metal memory). Other backends (XNNPACK/CoreML) + // pass 0 → original one-shot behavior. TextPrefiller(TextDecoderRunner *text_decoder_runner, bool use_kv_cache, - bool enable_parallel_prefill, int64_t max_seq_len = 128); + bool enable_parallel_prefill, int64_t max_seq_len = 128, + int32_t prefill_chunk_size = 0); virtual ~TextPrefiller() = default; /** @@ -70,6 +76,7 @@ class TextPrefiller { bool use_kv_cache_; bool enable_parallel_prefill_; int64_t max_seq_len_; + int32_t prefill_chunk_size_; }; } // namespace llm diff --git a/packages/react-native-executorch/common/runner/text_runner.cpp b/packages/react-native-executorch/common/runner/text_runner.cpp index 348f0bfb82..bbcc0d8981 100644 --- a/packages/react-native-executorch/common/runner/text_runner.cpp +++ b/packages/react-native-executorch/common/runner/text_runner.cpp @@ -26,11 +26,24 @@ Error TextRunner::load_subcomponents() { Stats *stats_ptr = &stats_; - text_decoder_runner_ = std::make_unique( - *module_, io_manager_.get(), config_); + text_decoder_runner_ = + std::make_unique(*module_, io_manager_.get(), config_); + + int32_t prefill_chunk_size = 0; + auto fwd_meta = module_->method_meta("forward"); + if (fwd_meta.ok() && fwd_meta->uses_backend("MLXBackend")) { + auto input_meta = fwd_meta->input_tensor_meta(0); + if (input_meta.ok()) { + auto sizes = input_meta->sizes(); + if (sizes.size() >= 2 && sizes[sizes.size() - 1] > 0) { + prefill_chunk_size = sizes[sizes.size() - 1]; + } + } + } + text_prefiller_ = std::make_unique( text_decoder_runner_.get(), config_.enable_kv_cache, - config_.enable_dynamic_shape, config_.max_seq_len); + config_.enable_dynamic_shape, config_.max_seq_len, prefill_chunk_size); text_token_generator_ = std::make_unique( tokenizer_.get(), text_decoder_runner_.get(), config_.enable_kv_cache, std::move(eos_ids_), stats_ptr, config_); @@ -83,10 +96,12 @@ Error TextRunner::generate_internal( std::vector prompt_tokens = encodeResult.get(); int num_prompt_tokens = prompt_tokens.size(); - // For dynamic-shape PTEs (Gemma4 iter*), get_max_seq_len is the per-call - // decoder chunk size (e.g. 128) and the true generation budget lives in - // get_max_context_len. Static-shape PTEs set both equal, so this collapses - // to the old behavior. Mirrors multimodal_prefiller.cpp:96. + // For dynamic-shape PTEs (e.g. Gemma4 MLX/Vulkan), get_max_seq_len is the + // per-call decoder chunk size (e.g. the sliding window) and the real + // generation budget lives in get_max_context_len. Static-shape PTEs set both + // equal, so this collapses to the old behavior. Without this the budget is + // computed from the small chunk size, so max_new_tokens can resolve to ~0 and + // generation ends immediately after prefill. const int32_t seq_cap = config_.enable_dynamic_shape ? config_.max_context_length : config_.max_seq_len; diff --git a/packages/react-native-executorch/react-native-executorch.podspec b/packages/react-native-executorch/react-native-executorch.podspec index 849759243f..daf0da27c7 100644 --- a/packages/react-native-executorch/react-native-executorch.podspec +++ b/packages/react-native-executorch/react-native-executorch.podspec @@ -62,6 +62,12 @@ Pod::Spec.new do |s| s.libraries = "z" s.ios.vendored_frameworks = "third-party/ios/ExecutorchLib.xcframework" + + # NOTE: mlx.metallib (the MLX GPU kernels) is bundled INSIDE + # ExecutorchLib.framework, colocated with the binary that contains the MLX + # code. MLX's runtime loader resolves the metallib relative to that binary + # (via dladdr), so it must live next to it in the framework — not at the app + # bundle root. # Exclude file with tests to not introduce gtest dependency. # Do not include the headers from common/rnexecutorch/jsi/ as source files. # Xcode/Cocoapods leaks them to other pods that an app also depends on, so if diff --git a/packages/react-native-executorch/src/constants/modelRegistry.ts b/packages/react-native-executorch/src/constants/modelRegistry.ts index bd04a37883..c3cda78498 100644 --- a/packages/react-native-executorch/src/constants/modelRegistry.ts +++ b/packages/react-native-executorch/src/constants/modelRegistry.ts @@ -38,7 +38,7 @@ import { RnExecutorchErrorCode } from '../errors/ErrorCodes'; * compile-time error. * @category Utils */ -export type Backend = 'xnnpack' | 'coreml' | 'vulkan' | 'qnn'; +export type Backend = 'xnnpack' | 'coreml' | 'vulkan' | 'qnn' | 'mlx'; /** * Options for a `models` accessor call. @@ -78,7 +78,7 @@ type ConfigOf = Extract< >; type BackendsOf = Extract; -const BACKEND_ORDER: Backend[] = ['xnnpack', 'coreml', 'vulkan', 'qnn']; +const BACKEND_ORDER: Backend[] = ['xnnpack', 'coreml', 'mlx', 'vulkan', 'qnn']; function firstBackend(variants: AnyVariantMap): Backend { for (const b of BACKEND_ORDER) { @@ -181,6 +181,33 @@ function tts(c: C): () => C { // Per-backend variant maps for models that ship more than one backend. // ───────────────────────────────────────────────────────────────────────────── +const GEMMA4_E2B_VARIANTS = { + mlx: { + base: { + modelName: 'gemma4-e2b' as const, + modelSource: M.GEMMA4_E2B_MLX_MODEL, + tokenizerSource: M.GEMMA4_E2B_TOKENIZER, + tokenizerConfigSource: M.GEMMA4_E2B_TOKENIZER_CONFIG, + }, + }, + xnnpack: { + base: { + modelName: 'gemma4-e2b' as const, + modelSource: M.GEMMA4_E2B_XNNPACK_MODEL, + tokenizerSource: M.GEMMA4_E2B_TOKENIZER, + tokenizerConfigSource: M.GEMMA4_E2B_TOKENIZER_CONFIG, + }, + }, + vulkan: { + base: { + modelName: 'gemma4-e2b' as const, + modelSource: M.GEMMA4_E2B_VULKAN_MODEL, + tokenizerSource: M.GEMMA4_E2B_TOKENIZER, + tokenizerConfigSource: M.GEMMA4_E2B_TOKENIZER_CONFIG, + }, + }, +}; + const EFFICIENTNET_V2_S_VARIANTS = { xnnpack: { base: { @@ -496,7 +523,10 @@ export const models = { M.LFM2_5_1_2B_INSTRUCT_QUANTIZED ), bielik_v3_0_1_5b: pair(M.BIELIK_V3_0_1_5B, M.BIELIK_V3_0_1_5B_QUANTIZED), - gemma4_e2b: base(M.GEMMA4_E2B), + gemma4_e2b: variant(GEMMA4_E2B_VARIANTS, { + ios: 'mlx', + android: 'vulkan', + }), // Multimodal LLMs — same hook/module as plain LLMs, listed here so users // pick a model by capability ("LLM") rather than by modality. lfm2_5_vl_1_6b: base(M.LFM2_5_VL_1_6B_QUANTIZED), diff --git a/packages/react-native-executorch/src/constants/modelUrls.ts b/packages/react-native-executorch/src/constants/modelUrls.ts index b14372d145..8ff13f585c 100644 --- a/packages/react-native-executorch/src/constants/modelUrls.ts +++ b/packages/react-native-executorch/src/constants/modelUrls.ts @@ -124,34 +124,40 @@ export const QWEN3_0_6B_QUANTIZED = { generationConfig: QWEN3_GENERATION_CONFIG, } as const; -// GEMMA 4 -const GEMMA4_E2B_XNNPACK = `${URL_PREFIX}-gemma-4/${PREVIOUS_VERSION_TAG}/e2b/xnnpack/gemma_4_e2b_xnnpack_8da4w.pte`; -const GEMMA4_E2B_VULKAN = `${URL_PREFIX}-gemma-4/${PREVIOUS_VERSION_TAG}/e2b/vulkan/gemma_4_e2b_vulkan_8da4w.pte`; -const GEMMA4_E2B_XNNPACK_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/xnnpack/gemma_4_e2b_xnnpack_8da4w.pte`; +// GEMMA 4 — separate HF repo; tokenizer files live at the e2b root and are +// shared by all backend variants. +const GEMMA4_E2B_PREFIX = `${URL_PREFIX}-gemma-4/${PREVIOUS_VERSION_TAG}/e2b`; +export const GEMMA4_E2B_MLX_MODEL = `${GEMMA4_E2B_PREFIX}/mlx/gemma4_e2b_mlx_int4.pte`; +export const GEMMA4_E2B_XNNPACK_MODEL = `${GEMMA4_E2B_PREFIX}/xnnpack/gemma_4_e2b_xnnpack_8da4w.pte`; +export const GEMMA4_E2B_VULKAN_MODEL = `${GEMMA4_E2B_PREFIX}/vulkan/gemma_4_e2b_vulkan_8da4w.pte`; +export const GEMMA4_E2B_TOKENIZER = `${GEMMA4_E2B_PREFIX}/tokenizer.json`; +export const GEMMA4_E2B_TOKENIZER_CONFIG = `${GEMMA4_E2B_PREFIX}/tokenizer_config.json`; + +const GEMMA4_E2B_MODEL = + Platform.OS === `android` ? GEMMA4_E2B_VULKAN_MODEL : GEMMA4_E2B_MLX_MODEL; + +const GEMMA4_E2B_MLX_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/mlx/gemma4_e2b_mlx_int4.pte`; const GEMMA4_E2B_VULKAN_MM = `${URL_PREFIX}-gemma-4-multimodal/${PREVIOUS_VERSION_TAG}/e2b/vulkan/gemma_4_e2b_vulkan_8da4w.pte`; -const GEMMA4_TOKENIZER = `${URL_PREFIX}-gemma-4/${PREVIOUS_VERSION_TAG}/e2b/tokenizer.json`; -const GEMMA4_TOKENIZER_CONFIG = `${URL_PREFIX}-gemma-4/${PREVIOUS_VERSION_TAG}/e2b/tokenizer_config.json`; /** * @category Models - LLM */ export const GEMMA4_E2B = { modelName: 'gemma4-e2b', - modelSource: - Platform.OS === `android` ? GEMMA4_E2B_VULKAN : GEMMA4_E2B_XNNPACK, - tokenizerSource: GEMMA4_TOKENIZER, - tokenizerConfigSource: GEMMA4_TOKENIZER_CONFIG, + modelSource: GEMMA4_E2B_MODEL, + tokenizerSource: GEMMA4_E2B_TOKENIZER, + tokenizerConfigSource: GEMMA4_E2B_TOKENIZER_CONFIG, } as const; /** - * @category Models - VLM + * @category Models - LLM Multimodal */ export const GEMMA4_E2B_MM = { modelName: 'gemma4-e2b-multimodal', modelSource: - Platform.OS === `android` ? GEMMA4_E2B_VULKAN_MM : GEMMA4_E2B_XNNPACK_MM, - tokenizerSource: GEMMA4_TOKENIZER, - tokenizerConfigSource: GEMMA4_TOKENIZER_CONFIG, + Platform.OS === `android` ? GEMMA4_E2B_VULKAN_MM : GEMMA4_E2B_MLX_MM, + tokenizerSource: GEMMA4_E2B_TOKENIZER, + tokenizerConfigSource: GEMMA4_E2B_TOKENIZER_CONFIG, capabilities: ['vision', 'audio'], audioConfig: { samplesPerBlock: 7680, diff --git a/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so b/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so index 1e882b92fa..36cb0919c2 100644 Binary files a/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so and b/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so differ diff --git a/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so b/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so index 45efcf585d..7d39ee85a3 100644 Binary files a/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so and b/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so differ diff --git a/packages/react-native-executorch/third-party/include/executorch/ExecuTorch.h b/packages/react-native-executorch/third-party/include/executorch/ExecuTorch.h index 3a12a5ddba..d0ad6c2840 100644 --- a/packages/react-native-executorch/third-party/include/executorch/ExecuTorch.h +++ b/packages/react-native-executorch/third-party/include/executorch/ExecuTorch.h @@ -6,6 +6,8 @@ * LICENSE file in the root directory of this source tree. */ +#import "ExecuTorchBackendOption.h" +#import "ExecuTorchBackendOptionsMap.h" #import "ExecuTorchError.h" #import "ExecuTorchLog.h" #import "ExecuTorchModule.h" diff --git a/packages/react-native-executorch/third-party/include/executorch/ExecuTorchModule.h b/packages/react-native-executorch/third-party/include/executorch/ExecuTorchModule.h index 823e5cf5cb..51b8abfa68 100644 --- a/packages/react-native-executorch/third-party/include/executorch/ExecuTorchModule.h +++ b/packages/react-native-executorch/third-party/include/executorch/ExecuTorchModule.h @@ -6,6 +6,8 @@ * LICENSE file in the root directory of this source tree. */ +#import "ExecuTorchBackendOption.h" +#import "ExecuTorchBackendOptionsMap.h" #import "ExecuTorchValue.h" NS_ASSUME_NONNULL_BEGIN @@ -198,6 +200,37 @@ NS_SWIFT_NAME(Module) */ - (BOOL)load:(NSError **)error; +/** + * Loads the module's program with per-delegate backend options. + * + * The receiver retains @c options for as long as the underlying program + * references it (lifetime tracked via ARC). + * + * @param options A `ExecuTorchBackendOptionsMap` containing per-delegate + * load-time configuration, built once via + * `[ExecuTorchBackendOptionsMap mapWithOptions:error:]`. + * @param verification The verification level to apply when loading the program. + * @param error A pointer to an NSError pointer that will be set if an error + * occurs. + * @return YES if the program was successfully loaded; otherwise, NO. + */ +- (BOOL)loadWithOptions:(ExecuTorchBackendOptionsMap *)options + verification:(ExecuTorchVerification)verification + error:(NSError **)error NS_REFINED_FOR_SWIFT; + +/** + * Loads the module's program with per-delegate backend options using minimal + * verification. + * + * @param options A `ExecuTorchBackendOptionsMap` containing per-delegate + * load-time configuration. + * @param error A pointer to an NSError pointer that will be set if an error + * occurs. + * @return YES if the program was successfully loaded; otherwise, NO. + */ +- (BOOL)loadWithOptions:(ExecuTorchBackendOptionsMap *)options + error:(NSError **)error NS_REFINED_FOR_SWIFT; + /** * Checks if the module is loaded. * @@ -215,6 +248,19 @@ NS_SWIFT_NAME(Module) - (BOOL)loadMethod:(NSString *)methodName error:(NSError **)error NS_SWIFT_NAME(load(_:)); +/** + * Loads a specific method from the program with per-delegate backend options. + * + * @param methodName A string representing the name of the method to load. + * @param options A `ExecuTorchBackendOptionsMap` containing per-delegate + * load-time configuration. + * @param error A pointer to an NSError pointer that is set if an error occurs. + * @return YES if the method was successfully loaded; otherwise, NO. + */ +- (BOOL)loadMethod:(NSString *)methodName + options:(ExecuTorchBackendOptionsMap *)options + error:(NSError **)error NS_REFINED_FOR_SWIFT; + /** * Checks if a specific method is loaded. * diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/buffer_data_loader.h b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/buffer_data_loader.h index 38322aff43..b3744d0970 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/buffer_data_loader.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/buffer_data_loader.h @@ -36,9 +36,10 @@ class BufferDataLoader final : public executorch::runtime::DataLoader { ET_UNUSED const DataLoader::SegmentInfo &segment_info) const override { size_t total_size; bool overflow = c10::add_overflows(offset, size, &total_size); - ET_CHECK_OR_RETURN_ERROR(!overflow && total_size <= size_, InvalidArgument, - "offset %zu + size %zu > size_ %zu", offset, size, - size_); + ET_CHECK_OR_RETURN_ERROR( + !overflow && total_size <= size_, InvalidArgument, + "offset %zu + size %zu > size_ %zu, or overflow detected", offset, size, + size_); return executorch::runtime::FreeableBuffer(data_ + offset, size, /*free_fn=*/nullptr); } diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mman.h b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mman.h index 788560c168..c35fd07f46 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mman.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mman.h @@ -17,6 +17,7 @@ #ifndef _WIN32 +#include #include #include @@ -41,6 +42,34 @@ ET_INLINE off_t get_mmap_offset(size_t offset) { return static_cast(offset); } +/** + * Hint the kernel to prefetch pages eagerly and to optimize for sequential + * reads. Intended to reduce page-fault stutter during model initialization + * when the caller does not want to mlock the pages into RAM. + */ +ET_INLINE void madvise_pages_willneed_sequential(void *addr, size_t len) { + ::madvise(addr, len, MADV_WILLNEED); + ::madvise(addr, len, MADV_SEQUENTIAL); +} + +/** + * On Apple platforms, schedule kernel read-ahead on the file descriptor itself + * via fcntl(F_RDADVISE). This is more aggressive than madvise for cold starts: + * it brings pages into the unified buffer cache so first-touch faults are + * serviced from RAM instead of storage. No-op on non-Apple POSIX platforms. + */ +ET_INLINE void fcntl_rdadvise_apple(int fd, size_t file_size) { +#if defined(__APPLE__) + struct radvisory advice; + advice.ra_offset = 0; + advice.ra_count = static_cast(file_size); + ::fcntl(fd, F_RDADVISE, &advice); +#else + (void)fd; + (void)file_size; +#endif +} + #else #define NOMINMAX @@ -78,4 +107,21 @@ ET_INLINE uint64_t get_mmap_offset(size_t offset) { return static_cast(offset); } +/** + * No-op on Windows: there is no direct equivalent to madvise(MADV_WILLNEED | + * MADV_SEQUENTIAL) and the existing mman_windows shim does not implement one. + */ +ET_INLINE void madvise_pages_willneed_sequential(void *addr, size_t len) { + (void)addr; + (void)len; +} + +/** + * No-op on Windows: F_RDADVISE is an Apple-specific fcntl command. + */ +ET_INLINE void fcntl_rdadvise_apple(int fd, size_t file_size) { + (void)fd; + (void)file_size; +} + #endif diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mmap_data_loader.h b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mmap_data_loader.h index cbc2ced2d9..1b648ce35f 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mmap_data_loader.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/mmap_data_loader.h @@ -38,6 +38,10 @@ class MmapDataLoader final : public executorch::runtime::DataLoader { UseMlock, /// Call `mlock()` on loaded pages, ignoring errors if it fails. UseMlockIgnoreErrors, + /// Use madvise(MADV_WILLNEED | MADV_SEQUENTIAL) instead of mlock. + /// Tells the kernel to prefetch pages eagerly and optimize for + /// sequential reads, without pinning them in RAM. + UseMadvise, }; /** diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/shared_ptr_data_loader.h b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/shared_ptr_data_loader.h index dc0e179187..e9bb26322f 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/shared_ptr_data_loader.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/data_loader/shared_ptr_data_loader.h @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -32,9 +33,12 @@ class SharedPtrDataLoader final : public executorch::runtime::DataLoader { executorch::runtime::Result load(size_t offset, size_t size, ET_UNUSED const DataLoader::SegmentInfo &segment_info) const override { - ET_CHECK_OR_RETURN_ERROR(offset + size <= size_, InvalidArgument, - "offset %zu + size %zu > size_ %zu", offset, size, - size_); + size_t total_size; + bool overflow = c10::add_overflows(offset, size, &total_size); + ET_CHECK_OR_RETURN_ERROR( + !overflow && total_size <= size_, InvalidArgument, + "offset %zu + size %zu > size_ %zu, or overflow detected", offset, size, + size_); return executorch::runtime::FreeableBuffer( static_cast(data_.get()) + offset, size, /*free_fn=*/nullptr); diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/module/module.h b/packages/react-native-executorch/third-party/include/executorch/extension/module/module.h index 278f996ed2..9dadc846ee 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/module/module.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/module/module.h @@ -14,6 +14,8 @@ #include #include +#include +#include #include #ifdef USE_ATEN_LIB @@ -25,6 +27,7 @@ namespace executorch { namespace extension { +using ET_RUNTIME_NAMESPACE::Kernel; using ET_RUNTIME_NAMESPACE::Method; using ET_RUNTIME_NAMESPACE::MethodMeta; using ET_RUNTIME_NAMESPACE::NamedDataMap; @@ -51,6 +54,8 @@ class Module { MmapUseMlock, /// Use memory locking and ignore errors. MmapUseMlockIgnoreErrors, + /// Use mmap with madvise(MADV_WILLNEED | MADV_SEQUENTIAL) hints. + MmapUseMadvise, }; /** @@ -182,9 +187,18 @@ class Module { /** * Loads the program with per-delegate runtime options. * - * @param[in] backend_options A LoadBackendOptionsMap containing per-delegate - * load-time configuration options. The caller must ensure this object - * outlives any methods loaded with these options. + * The Module deep-copies `backend_options` into internal storage, so the + * caller may release the input (and any backing BackendOption arrays its + * Spans referenced) immediately after this call returns. Future lazy + * `load_method` calls (e.g. triggered by `forward`) consume the + * Module-owned copy. + * + * Transactional: on failure, the previously-installed backend options + * (if any) are left in place; the input is not committed. + * + * @param[in] backend_options A LoadBackendOptionsMap containing + * per-delegate load-time configuration options. Deep-copied into the + * Module on success; not retained on failure. * @param[in] verification The type of verification to do before returning * success. * @@ -195,6 +209,21 @@ class Module { const Program::Verification verification = Program::Verification::Minimal); + /** + * Returns the deep-copied LoadBackendOptionsMap most recently installed + * via `load(LoadBackendOptionsMap, ...)`. The returned reference is owned + * by the Module and remains valid until the next call to + * `load(LoadBackendOptionsMap, ...)` or until the Module is destroyed. + * + * If `load(LoadBackendOptionsMap, ...)` has never been called, returns a + * default-constructed (empty, `size() == 0`) map. + * + * @returns Const reference to the Module-owned LoadBackendOptionsMap. + */ + inline const LoadBackendOptionsMap &backend_options() const { + return backend_options_map_; + } + /** * Checks if the program is loaded. * @@ -246,7 +275,8 @@ class Module { load_method(const std::string &method_name, runtime::HierarchicalAllocator *planned_memory = nullptr, torch::executor::EventTracer *event_tracer = nullptr, - const LoadBackendOptionsMap *backend_options = nullptr); + const LoadBackendOptionsMap *backend_options = nullptr, + std::vector kernel_registry = {}); ET_DEPRECATED ET_NODISCARD runtime::Error inline load_method( const std::string &method_name, @@ -294,9 +324,10 @@ class Module { ET_NODISCARD inline runtime::Error load_forward(runtime::HierarchicalAllocator *planned_memory = nullptr, torch::executor::EventTracer *event_tracer = nullptr, - const LoadBackendOptionsMap *backend_options = nullptr) { - return load_method("forward", planned_memory, event_tracer, - backend_options); + const LoadBackendOptionsMap *backend_options = nullptr, + std::vector kernel_registry = {}) { + return load_method("forward", planned_memory, event_tracer, backend_options, + std::move(kernel_registry)); } ET_DEPRECATED ET_NODISCARD inline runtime::Error @@ -678,6 +709,7 @@ class Module { std::unique_ptr planned_memory; std::unique_ptr memory_manager; std::unique_ptr method; + std::vector kernel_registry; }; std::string file_path_; @@ -693,7 +725,14 @@ class Module { std::unique_ptr merged_data_map_; std::vector> shared_arenas_; ET_DEPRECATED std::vector debug_buffer_; - const LoadBackendOptionsMap *backend_options_ = nullptr; + // Module-owned deep-copy of the backend options most recently installed + // via load(LoadBackendOptionsMap, ...). `backend_options_storage_` owns + // the per-backend BackendOption arrays; `backend_options_map_` is a + // LoadBackendOptionsMap whose Spans reference those owned arrays. An + // empty map (`size() == 0`) is observationally indistinguishable from + // "never set" by downstream consumers, so we don't track that bit. + std::vector> backend_options_storage_; + LoadBackendOptionsMap backend_options_map_; bool share_memory_arenas_; ET_NODISCARD runtime::Error diff --git a/packages/react-native-executorch/third-party/include/executorch/extension/tensor/tensor_ptr.h b/packages/react-native-executorch/third-party/include/executorch/extension/tensor/tensor_ptr.h index 3dd4e890d3..15321ae608 100644 --- a/packages/react-native-executorch/third-party/include/executorch/extension/tensor/tensor_ptr.h +++ b/packages/react-native-executorch/third-party/include/executorch/extension/tensor/tensor_ptr.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -105,13 +106,21 @@ make_tensor_ptr(std::vector sizes, executorch::aten::ScalarType type = deduced_type, executorch::aten::TensorShapeDynamism dynamism = executorch::aten::TensorShapeDynamism::DYNAMIC_BOUND) { - ET_CHECK_MSG(data.size() == - executorch::aten::compute_numel(sizes.data(), sizes.size()), + auto numel_result = executorch::aten::safe_numel(sizes.data(), sizes.size()); + ET_CHECK_MSG(numel_result.ok(), "safe_numel failed: %d", + static_cast(numel_result.error())); + ET_CHECK_MSG(data.size() == static_cast(numel_result.get()), "Data size does not match tensor size."); if (type != deduced_type) { ET_CHECK_MSG(runtime::canCast(deduced_type, type), "Cannot cast deduced type to specified type."); - std::vector casted_data(data.size() * aten::elementSize(type)); + size_t casted_bytes = 0; + ET_CHECK_MSG(!c10::mul_overflows( + data.size(), static_cast(aten::elementSize(type)), + &casted_bytes), + "casted_data size overflow: %zu elements * %zu bytes/element", + data.size(), static_cast(aten::elementSize(type))); + std::vector casted_data(casted_bytes); // Create a minimal context for error handling in ET_SWITCH struct { @@ -327,8 +336,11 @@ make_tensor_ptr(const executorch::aten::Tensor &tensor, const auto same_rank = sizes.size() == static_cast(tensor.dim()); const auto same_shape = same_rank && std::equal(sizes.begin(), sizes.end(), tensor.sizes().begin()); - const auto element_count = - executorch::aten::compute_numel(sizes.data(), sizes.size()); + auto element_count_result = + executorch::aten::safe_numel(sizes.data(), sizes.size()); + ET_CHECK_MSG(element_count_result.ok(), "safe_numel failed: %d", + static_cast(element_count_result.error())); + const auto element_count = element_count_result.get(); const auto parent_element_count = tensor.numel(); ET_CHECK_MSG( element_count <= parent_element_count, diff --git a/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/Functions.h b/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/Functions.h index 1b9b4a44b3..cd934e7997 100644 --- a/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/Functions.h +++ b/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/Functions.h @@ -91,6 +91,12 @@ TORCH_API inline torch::executor::Tensor & gelu_outf(torch::executor::KernelRunt } +// aten::grid_sampler_2d.out(Tensor input, Tensor grid, int interpolation_mode, int padding_mode, bool align_corners, *, Tensor(a!) out) -> Tensor(a!) +TORCH_API inline torch::executor::Tensor & grid_sampler_2d_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & input, const torch::executor::Tensor & grid, int64_t interpolation_mode, int64_t padding_mode, bool align_corners, torch::executor::Tensor & out) { + return ::torch::executor::native::opt_grid_sampler_2d_out(context, input, grid, interpolation_mode, padding_mode, align_corners, out); +} + + // aten::le.Scalar_out(Tensor self, Scalar other, *, Tensor(a!) out) -> Tensor(a!) TORCH_API inline torch::executor::Tensor & le_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, const torch::executor::Scalar & other, torch::executor::Tensor & out) { return ::torch::executor::native::opt_le_scalar_out(context, self, other, out); @@ -139,6 +145,12 @@ TORCH_API inline torch::executor::Tensor & sub_outf(torch::executor::KernelRunti } +// aten::sum.IntList_out(Tensor self, int[1]? dim, bool keepdim=False, *, ScalarType? dtype=None, Tensor(a!) out) -> Tensor(a!) +TORCH_API inline torch::executor::Tensor & sum_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, bool keepdim, torch::executor::optional dtype, torch::executor::Tensor & out) { + return ::torch::executor::native::opt_sum_dim_out(context, self, dim, keepdim, dtype, out); +} + + // aten::sub.Scalar_out(Tensor self, Scalar other, Scalar alpha=1, *, Tensor(a!) out) -> Tensor(a!) TORCH_API inline torch::executor::Tensor & sub_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, const torch::executor::Scalar & other, const torch::executor::Scalar & alpha, torch::executor::Tensor & out) { return ::torch::executor::native::opt_sub_scalar_out(context, self, other, alpha, out); diff --git a/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/NativeFunctions.h b/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/NativeFunctions.h index 3e83af3688..6dff2dae23 100644 --- a/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/NativeFunctions.h +++ b/packages/react-native-executorch/third-party/include/executorch/kernels/optimized/NativeFunctions.h @@ -42,6 +42,8 @@ torch::executor::Tensor & opt_exp_out(const torch::executor::Tensor & self, torc torch::executor::Tensor & opt_exp_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::Tensor & out); torch::executor::Tensor & opt_gelu_out(const torch::executor::Tensor & self, torch::executor::string_view approximate, torch::executor::Tensor & out); torch::executor::Tensor & opt_gelu_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::string_view approximate, torch::executor::Tensor & out); +torch::executor::Tensor & opt_grid_sampler_2d_out(const torch::executor::Tensor & input, const torch::executor::Tensor & grid, int64_t interpolation_mode, int64_t padding_mode, bool align_corners, torch::executor::Tensor & out); +torch::executor::Tensor & opt_grid_sampler_2d_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & input, const torch::executor::Tensor & grid, int64_t interpolation_mode, int64_t padding_mode, bool align_corners, torch::executor::Tensor & out); torch::executor::Tensor & opt_le_scalar_out(const torch::executor::Tensor & self, const torch::executor::Scalar & other, torch::executor::Tensor & out); torch::executor::Tensor & opt_le_scalar_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, const torch::executor::Scalar & other, torch::executor::Tensor & out); torch::executor::Tensor & opt_le_tensor_out(const torch::executor::Tensor & self, const torch::executor::Tensor & other, torch::executor::Tensor & out); @@ -58,6 +60,8 @@ ::std::tuple opt_native_layer_norm_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & input, torch::executor::ArrayRef normalized_shape, const torch::executor::optional & weight, const torch::executor::optional & bias, double eps, torch::executor::Tensor & out0, torch::executor::Tensor & out1, torch::executor::Tensor & out2); torch::executor::Tensor & opt_sub_out(const torch::executor::Tensor & self, const torch::executor::Tensor & other, const torch::executor::Scalar & alpha, torch::executor::Tensor & out); torch::executor::Tensor & opt_sub_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, const torch::executor::Tensor & other, const torch::executor::Scalar & alpha, torch::executor::Tensor & out); +torch::executor::Tensor & opt_sum_dim_out(const torch::executor::Tensor & self, torch::executor::optional> dim, bool keepdim, torch::executor::optional dtype, torch::executor::Tensor & out); +torch::executor::Tensor & opt_sum_dim_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, bool keepdim, torch::executor::optional dtype, torch::executor::Tensor & out); torch::executor::Tensor & opt_sub_scalar_out(const torch::executor::Tensor & self, const torch::executor::Scalar & other, const torch::executor::Scalar & alpha, torch::executor::Tensor & out); torch::executor::Tensor & opt_sub_scalar_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, const torch::executor::Scalar & other, const torch::executor::Scalar & alpha, torch::executor::Tensor & out); torch::executor::Tensor & opt_where_out(const torch::executor::Tensor & condition, const torch::executor::Tensor & self, const torch::executor::Tensor & other, torch::executor::Tensor & out); diff --git a/packages/react-native-executorch/third-party/include/executorch/kernels/portable/Functions.h b/packages/react-native-executorch/third-party/include/executorch/kernels/portable/Functions.h index b1911d7ae2..c96f3daf4b 100644 --- a/packages/react-native-executorch/third-party/include/executorch/kernels/portable/Functions.h +++ b/packages/react-native-executorch/third-party/include/executorch/kernels/portable/Functions.h @@ -25,12 +25,24 @@ namespace executor { namespace aten { +// aten::_adaptive_avg_pool2d.out(Tensor self, SymInt[2] output_size, *, Tensor(a!) out) -> Tensor(a!) +TORCH_API inline torch::executor::Tensor & _adaptive_avg_pool2d_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::ArrayRef output_size, torch::executor::Tensor & out) { + return ::torch::executor::native::_adaptive_avg_pool2d_out(context, self, output_size, out); +} + + // aten::_cdist_forward.out(Tensor x1, Tensor x2, float p, int? compute_mode, *, Tensor(a!) out) -> Tensor(a!) TORCH_API inline torch::executor::Tensor & _cdist_forward_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & x1, const torch::executor::Tensor & x2, double p, torch::executor::optional compute_mode, torch::executor::Tensor & out) { return ::torch::executor::native::_cdist_forward_out(context, x1, x2, p, compute_mode, out); } +// aten::_conj_physical.out(Tensor self, *, Tensor(a!) out) -> Tensor(a!) +TORCH_API inline torch::executor::Tensor & _conj_physical_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::Tensor & out) { + return ::torch::executor::native::_conj_physical_out(context, self, out); +} + + // aten::_log_softmax.out(Tensor self, int dim, bool half_to_float, *, Tensor(a!) out) -> Tensor(a!) TORCH_API inline torch::executor::Tensor & _log_softmax_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, int64_t dim, bool half_to_float, torch::executor::Tensor & out) { return ::torch::executor::native::log_softmax_out(context, self, dim, half_to_float, out); @@ -1201,6 +1213,12 @@ TORCH_API inline torch::executor::Tensor & var_outf(torch::executor::KernelRunti } +// aten::var_mean.correction_out(Tensor self, int[1]? dim=None, *, Scalar? correction=None, bool keepdim=False, Tensor(a!) out0, Tensor(b!) out1) -> (Tensor(a!), Tensor(b!)) +TORCH_API inline ::std::tuple var_mean_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, const torch::executor::optional & correction, bool keepdim, torch::executor::Tensor & out0, torch::executor::Tensor & out1) { + return ::torch::executor::native::var_mean_correction_out(context, self, dim, correction, keepdim, out0, out1); +} + + // aten::var.out(Tensor self, int[1]? dim, bool unbiased=True, bool keepdim=False, *, Tensor(a!) out) -> Tensor(a!) TORCH_API inline torch::executor::Tensor & var_outf(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, bool unbiased, bool keepdim, torch::executor::Tensor & out) { return ::torch::executor::native::var_out(context, self, dim, unbiased, keepdim, out); diff --git a/packages/react-native-executorch/third-party/include/executorch/kernels/portable/NativeFunctions.h b/packages/react-native-executorch/third-party/include/executorch/kernels/portable/NativeFunctions.h index c36896b7a1..e2a6cc3f52 100644 --- a/packages/react-native-executorch/third-party/include/executorch/kernels/portable/NativeFunctions.h +++ b/packages/react-native-executorch/third-party/include/executorch/kernels/portable/NativeFunctions.h @@ -20,8 +20,12 @@ namespace torch { namespace executor { namespace native { +torch::executor::Tensor & _adaptive_avg_pool2d_out(const torch::executor::Tensor & self, torch::executor::ArrayRef output_size, torch::executor::Tensor & out); +torch::executor::Tensor & _adaptive_avg_pool2d_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::ArrayRef output_size, torch::executor::Tensor & out); torch::executor::Tensor & _cdist_forward_out(const torch::executor::Tensor & x1, const torch::executor::Tensor & x2, double p, torch::executor::optional compute_mode, torch::executor::Tensor & out); torch::executor::Tensor & _cdist_forward_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & x1, const torch::executor::Tensor & x2, double p, torch::executor::optional compute_mode, torch::executor::Tensor & out); +torch::executor::Tensor & _conj_physical_out(const torch::executor::Tensor & self, torch::executor::Tensor & out); +torch::executor::Tensor & _conj_physical_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::Tensor & out); torch::executor::Tensor & log_softmax_out(const torch::executor::Tensor & self, int64_t dim, bool half_to_float, torch::executor::Tensor & out); torch::executor::Tensor & log_softmax_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, int64_t dim, bool half_to_float, torch::executor::Tensor & out); ::std::tuple _native_batch_norm_legit_out(const torch::executor::Tensor & input, const torch::executor::optional & weight, const torch::executor::optional & bias, torch::executor::Tensor & running_mean, torch::executor::Tensor & running_var, bool training, double momentum, double eps, torch::executor::Tensor & out, torch::executor::Tensor & save_mean, torch::executor::Tensor & save_invstd); @@ -412,6 +416,8 @@ torch::executor::Tensor & upsample_nearest2d_vec_out(const torch::executor::Tens torch::executor::Tensor & upsample_nearest2d_vec_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & input, torch::executor::optional> output_size, torch::executor::optional> scale_factors, torch::executor::Tensor & out); torch::executor::Tensor & var_correction_out(const torch::executor::Tensor & self, torch::executor::optional> dim, const torch::executor::optional & correction, bool keepdim, torch::executor::Tensor & out); torch::executor::Tensor & var_correction_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, const torch::executor::optional & correction, bool keepdim, torch::executor::Tensor & out); +::std::tuple var_mean_correction_out(const torch::executor::Tensor & self, torch::executor::optional> dim, const torch::executor::optional & correction, bool keepdim, torch::executor::Tensor & out0, torch::executor::Tensor & out1); +::std::tuple var_mean_correction_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, const torch::executor::optional & correction, bool keepdim, torch::executor::Tensor & out0, torch::executor::Tensor & out1); torch::executor::Tensor & var_out(const torch::executor::Tensor & self, torch::executor::optional> dim, bool unbiased, bool keepdim, torch::executor::Tensor & out); torch::executor::Tensor & var_out(torch::executor::KernelRuntimeContext & context, const torch::executor::Tensor & self, torch::executor::optional> dim, bool unbiased, bool keepdim, torch::executor::Tensor & out); torch::executor::Tensor & view_as_real_copy_out(const torch::executor::Tensor & self, torch::executor::Tensor & out); diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/backend/backend_options_map.h b/packages/react-native-executorch/third-party/include/executorch/runtime/backend/backend_options_map.h index 8ae9543c29..5acf6cca2d 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/backend/backend_options_map.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/backend/backend_options_map.h @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -168,6 +169,42 @@ class LoadBackendOptionsMap final { */ size_t size() const { return size_; } + /** + * Non-owning view of a single (backend_id, options) entry, returned by + * entry_at(). The pointer / span are valid until the map is mutated or + * destroyed. + */ + struct EntryView { + const char *backend_id = nullptr; + Span options; + }; + + /** + * Returns the (backend_id, options) entry at the given index for + * enumeration over the map's contents. + * + * @param index The entry index. Must be < size(); behavior is undefined + * otherwise. Use this together with size() to walk every entry. + * @return EntryView referencing the entry's backend_id and options. The + * view is valid until the next mutation of, or destruction of, this + * map. + * + * Example: + * @code + * for (size_t i = 0; i < map.size(); ++i) { + * const auto entry = map.entry_at(i); + * // use entry.backend_id and entry.options ... + * } + * @endcode + */ + EntryView entry_at(size_t index) const { + ET_DCHECK_MSG(index < size_, "entry_at index %zu out of bounds (size=%zu)", + index, size_); + return EntryView{entries_[index].backend_id, + Span(entries_[index].options.data(), + entries_[index].options.size())}; + } + private: static constexpr size_t kMaxBackends = 8; static constexpr size_t kMaxBackendIdLength = 64; diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/array_ref.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/array_ref.h index 01dd581663..867520137f 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/array_ref.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/array_ref.h @@ -30,6 +30,7 @@ #include #include +#include #include namespace executorch { @@ -146,7 +147,8 @@ template class ArrayRef final { /// slice(n, m) - Take M elements of the array starting at element N ArrayRef slice(size_t N, size_t M) const { // cant slice longer then the array - ET_CHECK(N + M <= size()); + size_t end = 0; + ET_CHECK(!c10::add_overflows(N, M, &end) && end <= size()); return ArrayRef(data() + N, M); } diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/error.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/error.h index 6500a6f907..c107dd43db 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/error.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/error.h @@ -152,6 +152,7 @@ constexpr const char *to_string(const Error error) { case Error::RegistrationAlreadyRegistered: return "Error::RegistrationAlreadyRegistered"; } + return "Error::Unknown"; } } // namespace runtime diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/evalue.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/evalue.h index b3b0689ba7..2e8bb5389e 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/evalue.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/evalue.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include @@ -67,6 +68,29 @@ template class BoxedEvalueList { */ executorch::aten::ArrayRef get() const; + /** + * Result-returning counterpart of get(). Validates each wrapped EValue's + * tag before materializing; returns Error::InvalidType if any element's + * tag does not match T and Error::InvalidState if any element pointer is + * null. Use this when materializing lists from untrusted .pte data so that + * a malformed program cannot force a process abort inside to() / + * ET_CHECK. + */ + Result> tryGet() const; + + /** + * Destroys the unwrapped elements without re-dereferencing wrapped_vals_. + * This is safe to call during EValue destruction because it does not + * dereference wrapped_vals_, which may point to EValues mutated by + * MoveCall instructions. + */ + void destroy_elements() { + for (typename executorch::aten::ArrayRef::size_type i = 0; + i < wrapped_vals_.size(); i++) { + unwrapped_vals_[i].~T(); + } + } + private: static EValue **checkWrappedVals(EValue **wrapped_vals, int size) { ET_CHECK_MSG(wrapped_vals != nullptr, "wrapped_vals cannot be null"); @@ -89,6 +113,10 @@ template <> executorch::aten::ArrayRef> BoxedEvalueList>::get() const; +template <> +Result>> +BoxedEvalueList>::tryGet() const; + // Aggregate typing system similar to IValue only slimmed down with less // functionality, no dependencies on atomic, and fewer supported types to better // suit embedded systems (ie no intrusive ptr) @@ -165,6 +193,13 @@ struct EValue { return payload.copyable_union.as_int; } + Result tryToInt() const { + if (!isInt()) { + return Error::InvalidType; + } + return payload.copyable_union.as_int; + } + /****** Double Type ******/ /*implicit*/ EValue(double d) : tag(Tag::Double) { payload.copyable_union.as_double = d; @@ -177,6 +212,13 @@ struct EValue { return payload.copyable_union.as_double; } + Result tryToDouble() const { + if (!isDouble()) { + return Error::InvalidType; + } + return payload.copyable_union.as_double; + } + /****** Bool Type ******/ /*implicit*/ EValue(bool b) : tag(Tag::Bool) { payload.copyable_union.as_bool = b; @@ -189,6 +231,13 @@ struct EValue { return payload.copyable_union.as_bool; } + Result tryToBool() const { + if (!isBool()) { + return Error::InvalidType; + } + return payload.copyable_union.as_bool; + } + /****** Scalar Type ******/ /// Construct an EValue using the implicit value of a Scalar. /*implicit*/ EValue(executorch::aten::Scalar s) { @@ -224,6 +273,19 @@ struct EValue { } } + Result tryToScalar() const { + if (isDouble()) { + return executorch::aten::Scalar(payload.copyable_union.as_double); + } + if (isInt()) { + return executorch::aten::Scalar(payload.copyable_union.as_int); + } + if (isBool()) { + return executorch::aten::Scalar(payload.copyable_union.as_bool); + } + return Error::InvalidType; + } + /****** Tensor Type ******/ /*implicit*/ EValue(executorch::aten::Tensor t) : tag(Tag::Tensor) { // When built in aten mode, at::Tensor has a non trivial constructor @@ -270,6 +332,16 @@ struct EValue { return payload.as_tensor; } + // Returns a copy of the Tensor handle (one intrusive_ptr refcount bump in + // ATen mode; free in lean mode). Unlike toTensor()'s const& / & overloads, + // tryToTensor() cannot return a reference — Result wraps by value. + Result tryToTensor() const { + if (!isTensor()) { + return Error::InvalidType; + } + return payload.as_tensor; + } + /****** String Type ******/ /*implicit*/ EValue(executorch::aten::ArrayRef *s) : tag(Tag::String) { ET_CHECK_MSG(s != nullptr, "ArrayRef pointer cannot be null"); @@ -286,6 +358,17 @@ struct EValue { payload.copyable_union.as_string_ptr->size()); } + Result tryToString() const { + if (!isString()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_string_ptr == nullptr) { + return Error::InvalidState; + } + return std::string_view(payload.copyable_union.as_string_ptr->data(), + payload.copyable_union.as_string_ptr->size()); + } + /****** Int List Type ******/ /*implicit*/ EValue(BoxedEvalueList *i) : tag(Tag::ListInt) { ET_CHECK_MSG(i != nullptr, @@ -302,6 +385,16 @@ struct EValue { return (payload.copyable_union.as_int_list_ptr)->get(); } + Result> tryToIntList() const { + if (!isIntList()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_int_list_ptr == nullptr) { + return Error::InvalidState; + } + return (payload.copyable_union.as_int_list_ptr)->tryGet(); + } + /****** Bool List Type ******/ /*implicit*/ EValue(executorch::aten::ArrayRef *b) : tag(Tag::ListBool) { @@ -318,6 +411,16 @@ struct EValue { return *(payload.copyable_union.as_bool_list_ptr); } + Result> tryToBoolList() const { + if (!isBoolList()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_bool_list_ptr == nullptr) { + return Error::InvalidState; + } + return *(payload.copyable_union.as_bool_list_ptr); + } + /****** Double List Type ******/ /*implicit*/ EValue(executorch::aten::ArrayRef *d) : tag(Tag::ListDouble) { @@ -334,6 +437,16 @@ struct EValue { return *(payload.copyable_union.as_double_list_ptr); } + Result> tryToDoubleList() const { + if (!isDoubleList()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_double_list_ptr == nullptr) { + return Error::InvalidState; + } + return *(payload.copyable_union.as_double_list_ptr); + } + /****** Tensor List Type ******/ /*implicit*/ EValue(BoxedEvalueList *t) : tag(Tag::ListTensor) { @@ -351,6 +464,17 @@ struct EValue { return payload.copyable_union.as_tensor_list_ptr->get(); } + Result> + tryToTensorList() const { + if (!isTensorList()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_tensor_list_ptr == nullptr) { + return Error::InvalidState; + } + return payload.copyable_union.as_tensor_list_ptr->tryGet(); + } + /****** List Optional Tensor Type ******/ /*implicit*/ EValue( BoxedEvalueList> *t) @@ -371,6 +495,17 @@ struct EValue { return payload.copyable_union.as_list_optional_tensor_ptr->get(); } + Result>> + tryToListOptionalTensor() const { + if (!isListOptionalTensor()) { + return Error::InvalidType; + } + if (payload.copyable_union.as_list_optional_tensor_ptr == nullptr) { + return Error::InvalidState; + } + return payload.copyable_union.as_list_optional_tensor_ptr->tryGet(); + } + /****** ScalarType Type ******/ executorch::aten::ScalarType toScalarType() const { ET_CHECK_MSG(isInt(), "EValue is not a ScalarType."); @@ -378,6 +513,14 @@ struct EValue { payload.copyable_union.as_int); } + Result tryToScalarType() const { + if (!isInt()) { + return Error::InvalidType; + } + return static_cast( + payload.copyable_union.as_int); + } + /****** MemoryFormat Type ******/ executorch::aten::MemoryFormat toMemoryFormat() const { ET_CHECK_MSG(isInt(), "EValue is not a MemoryFormat."); @@ -385,12 +528,27 @@ struct EValue { payload.copyable_union.as_int); } + Result tryToMemoryFormat() const { + if (!isInt()) { + return Error::InvalidType; + } + return static_cast( + payload.copyable_union.as_int); + } + /****** Layout Type ******/ executorch::aten::Layout toLayout() const { ET_CHECK_MSG(isInt(), "EValue is not a Layout."); return static_cast(payload.copyable_union.as_int); } + Result tryToLayout() const { + if (!isInt()) { + return Error::InvalidType; + } + return static_cast(payload.copyable_union.as_int); + } + /****** Device Type ******/ executorch::aten::Device toDevice() const { ET_CHECK_MSG(isInt(), "EValue is not a Device."); @@ -399,12 +557,29 @@ struct EValue { -1); } + Result tryToDevice() const { + if (!isInt()) { + return Error::InvalidType; + } + return executorch::aten::Device(static_cast( + payload.copyable_union.as_int), + -1); + } + template T to() &&; template typename internal::evalue_to_const_ref_overload_return::type to() const &; template typename internal::evalue_to_ref_overload_return::type to() &; + /** + * Result-returning equivalent of `to()`. Tag mismatch returns + * `Error::InvalidType`; a null list/string payload returns + * `Error::InvalidState`. Specializations are defined below via + * `EVALUE_DEFINE_TRY_TO`. + */ + template Result tryTo() const; + /** * Converts the EValue to an optional object that can represent both T and * an uninitialized state. @@ -416,6 +591,22 @@ struct EValue { return this->to(); } + /** + * Result-returning equivalent of `toOptional()`. None maps to an empty + * optional; any other tag that doesn't match T propagates `tryTo()`'s + * error (`Error::InvalidType`). + */ + template inline Result> tryToOptional() const { + if (this->isNone()) { + return std::optional(std::nullopt); + } + auto r = this->tryTo(); + if (!r.ok()) { + return r.error(); + } + return std::optional(std::move(r.get())); + } + private: // Pre cond: the payload value has had its destructor called void clearToNone() noexcept { @@ -446,17 +637,10 @@ struct EValue { payload.as_tensor.~Tensor(); } else if (isTensorList() && payload.copyable_union.as_tensor_list_ptr != nullptr) { - // for (auto& tensor : toTensorList()) { - for (auto &tensor : payload.copyable_union.as_tensor_list_ptr->get()) { - tensor.~Tensor(); - } + payload.copyable_union.as_tensor_list_ptr->destroy_elements(); } else if (isListOptionalTensor() && payload.copyable_union.as_list_optional_tensor_ptr != nullptr) { - // for (auto& optional_tensor : toListOptionalTensor()) { - for (auto &optional_tensor : - payload.copyable_union.as_list_optional_tensor_ptr->get()) { - optional_tensor.~optional(); - } + payload.copyable_union.as_list_optional_tensor_ptr->destroy_elements(); } } @@ -532,6 +716,53 @@ EVALUE_DEFINE_TO( toListOptionalTensor) #undef EVALUE_DEFINE_TO +#define EVALUE_DEFINE_TRY_TO(T, method_name) \ + template <> inline Result EValue::tryTo() const { \ + return this->method_name(); \ + } + +EVALUE_DEFINE_TRY_TO(executorch::aten::Scalar, tryToScalar) +EVALUE_DEFINE_TRY_TO(int64_t, tryToInt) +EVALUE_DEFINE_TRY_TO(bool, tryToBool) +EVALUE_DEFINE_TRY_TO(double, tryToDouble) +EVALUE_DEFINE_TRY_TO(std::string_view, tryToString) +EVALUE_DEFINE_TRY_TO(executorch::aten::ScalarType, tryToScalarType) +EVALUE_DEFINE_TRY_TO(executorch::aten::MemoryFormat, tryToMemoryFormat) +EVALUE_DEFINE_TRY_TO(executorch::aten::Layout, tryToLayout) +EVALUE_DEFINE_TRY_TO(executorch::aten::Device, tryToDevice) +// Tensor and Optional Tensor +EVALUE_DEFINE_TRY_TO(executorch::aten::Tensor, tryToTensor) +EVALUE_DEFINE_TRY_TO(std::optional, + tryToOptional) + +// IntList and Optional IntList +EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef, tryToIntList) +EVALUE_DEFINE_TRY_TO(std::optional>, + tryToOptional>) + +// DoubleList and Optional DoubleList +EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef, tryToDoubleList) +EVALUE_DEFINE_TRY_TO(std::optional>, + tryToOptional>) + +// BoolList and Optional BoolList +EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef, tryToBoolList) +EVALUE_DEFINE_TRY_TO(std::optional>, + tryToOptional>) + +// TensorList and Optional TensorList +EVALUE_DEFINE_TRY_TO(executorch::aten::ArrayRef, + tryToTensorList) +EVALUE_DEFINE_TRY_TO( + std::optional>, + tryToOptional>) + +// List of Optional Tensor +EVALUE_DEFINE_TRY_TO( + executorch::aten::ArrayRef>, + tryToListOptionalTensor) +#undef EVALUE_DEFINE_TRY_TO + template executorch::aten::ArrayRef BoxedEvalueList::get() const { for (typename executorch::aten::ArrayRef::size_type i = 0; @@ -542,6 +773,22 @@ executorch::aten::ArrayRef BoxedEvalueList::get() const { return executorch::aten::ArrayRef{unwrapped_vals_, wrapped_vals_.size()}; } +template +Result> BoxedEvalueList::tryGet() const { + for (typename executorch::aten::ArrayRef::size_type i = 0; + i < wrapped_vals_.size(); i++) { + if (wrapped_vals_[i] == nullptr) { + return Error::InvalidState; + } + auto r = wrapped_vals_[i]->template tryTo(); + if (!r.ok()) { + return r.error(); + } + unwrapped_vals_[i] = std::move(r.get()); + } + return executorch::aten::ArrayRef{unwrapped_vals_, wrapped_vals_.size()}; +} + } // namespace runtime } // namespace executorch diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/exec_aten/exec_aten.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/exec_aten/exec_aten.h index b4a1b2721f..db5096a46a 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/exec_aten/exec_aten.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/exec_aten/exec_aten.h @@ -8,7 +8,10 @@ #pragma once +#include // @manual +#include // @manual #include // @manual +#include // @manual #include #ifdef USE_ATEN_LIB #include // @manual @@ -28,6 +31,7 @@ #include // @manual #include // @manual #include // @manual +#include // @manual #include // @manual #include #else // use executor @@ -107,6 +111,25 @@ inline ssize_t compute_numel(const SizesType *sizes, ssize_t dim) { c10::multiply_integers(c10::ArrayRef(sizes, dim))); } +inline ::executorch::runtime::Result safe_numel(const SizesType *sizes, + ssize_t dim) { + ET_CHECK_OR_RETURN_ERROR(dim == 0 || sizes != nullptr, InvalidArgument, + "Sizes must be provided for non-scalar tensors"); + ssize_t numel = 1; + for (ssize_t i = 0; i < dim; i++) { + ET_CHECK_OR_RETURN_ERROR( + sizes[i] >= 0, InvalidArgument, + "Size must be non-negative, got %zd at dimension %zd", + static_cast(sizes[i]), i); + ssize_t next_numel; + ET_CHECK_OR_RETURN_ERROR( + !c10::mul_overflows(numel, static_cast(sizes[i]), &next_numel), + InvalidArgument, "Overflow computing numel at dimension %zd", i); + numel = next_numel; + } + return numel; +} + #undef ET_PRI_TENSOR_SIZE #define ET_PRI_TENSOR_SIZE PRId64 @@ -153,6 +176,7 @@ using OptionalArrayRef = using OptionalIntArrayRef = OptionalArrayRef; using torch::executor::compute_numel; +using torch::executor::safe_numel; #endif // Use ExecuTorch types diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/hierarchical_allocator.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/hierarchical_allocator.h index 9e2335501b..e061c9d182 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/hierarchical_allocator.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/hierarchical_allocator.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include @@ -56,17 +57,19 @@ class HierarchicalAllocator final { size_t offset_bytes, size_t size_bytes) { // Check for integer overflow in offset_bytes + size_bytes. - ET_CHECK_OR_RETURN_ERROR(size_bytes <= SIZE_MAX - offset_bytes, - InvalidArgument, - "Integer overflow in offset_bytes (%" ET_PRIsize_t - ") + size_bytes (%" ET_PRIsize_t ")", - offset_bytes, size_bytes); + size_t end_bytes = 0; + ET_CHECK_OR_RETURN_ERROR( + !c10::add_overflows(offset_bytes, size_bytes, &end_bytes), + InvalidArgument, + "Integer overflow in offset_bytes (%" ET_PRIsize_t + ") + size_bytes (%" ET_PRIsize_t ")", + offset_bytes, size_bytes); ET_CHECK_OR_RETURN_ERROR(memory_id < buffers_.size(), InvalidArgument, "id %" PRIu32 " >= %" ET_PRIsize_t, memory_id, buffers_.size()); Span buffer = buffers_[memory_id]; ET_CHECK_OR_RETURN_ERROR( - offset_bytes + size_bytes <= buffer.size(), MemoryAllocationFailed, + end_bytes <= buffer.size(), MemoryAllocationFailed, "offset_bytes (%" ET_PRIsize_t ") + size_bytes (%" ET_PRIsize_t ") >= allocator size (%" ET_PRIsize_t ") " "for memory_id %" PRIu32, diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/device.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/device.h index c6a42c55f0..32c53e9570 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/device.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/device.h @@ -26,7 +26,6 @@ enum class DeviceType : int8_t { constexpr size_t kNumDeviceTypes = 2; /// An index representing a specific device; e.g. GPU 0 vs GPU 1. -/// -1 means the default/unspecified device for that type. using DeviceIndex = int8_t; /** @@ -41,7 +40,7 @@ struct Device final { /// Constructs a new `Device` from a `DeviceType` and an optional device /// index. - /* implicit */ Device(DeviceType type, DeviceIndex index = -1) + /* implicit */ Device(DeviceType type, DeviceIndex index = 0) : type_(type), index_(index) {} /// Returns the type of device the tensor data resides on. @@ -50,7 +49,7 @@ struct Device final { /// Returns true if the device is of CPU type. bool is_cpu() const noexcept { return type_ == DeviceType::CPU; } - /// Returns the device index, or -1 if default/unspecified. + /// Returns the device index. DeviceIndex index() const noexcept { return index_; } bool operator==(const Device &other) const noexcept { @@ -63,7 +62,7 @@ struct Device final { private: DeviceType type_; - DeviceIndex index_ = -1; + DeviceIndex index_ = 0; }; } // namespace etensor diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/tensor_impl.h b/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/tensor_impl.h index 4f79070626..c841aef0c8 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/tensor_impl.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/core/portable_type/tensor_impl.h @@ -10,8 +10,11 @@ #include #include +#include #include +#include #include +#include // Forward declaration of a helper that provides access to internal resizing // methods of TensorImpl. Real definition is in @@ -99,11 +102,15 @@ class TensorImpl { * @param strides Strides of the tensor at each dimension. Must contain `dim` * entries. * @param dynamism The mutability of the shape of the tensor. + * @param device_type The type of device where tensor data resides. + * @param device_index The device index for multi-device scenarios. */ TensorImpl(ScalarType type, ssize_t dim, SizesType *sizes, void *data = nullptr, DimOrderType *dim_order = nullptr, StridesType *strides = nullptr, - TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC); + TensorShapeDynamism dynamism = TensorShapeDynamism::STATIC, + DeviceType device_type = DeviceType::CPU, + DeviceIndex device_index = 0); /** * Returns the size of the tensor in bytes. @@ -161,6 +168,15 @@ class TensorImpl { /// Returns the mutability of the shape of the tensor. TensorShapeDynamism shape_dynamism() const { return shape_dynamism_; } + /// Returns the device where tensor data resides. + Device device() const { return device_; } + + /// Returns the type of device where tensor data resides. + DeviceType device_type() const { return device_.type(); } + + /// Returns the device index, or 0 if default/unspecified. + DeviceIndex device_index() const { return device_.index(); } + /// Returns a pointer of type T to the constant underlying data blob. template inline const T *data() const { return static_cast(data()); @@ -238,6 +254,9 @@ class TensorImpl { /// Specifies the mutability of the shape of the tensor. const TensorShapeDynamism shape_dynamism_; + + /// Device where tensor data resides (CPU, CUDA, etc.) + Device device_; }; /** @@ -247,6 +266,16 @@ ssize_t compute_numel( const ::executorch::runtime::etensor::TensorImpl::SizesType *sizes, ssize_t dim); +/** + * Compute the number of elements based on the sizes of a tensor. + * Returns Error::InvalidArgument if any intermediate multiplication would + * overflow ssize_t, or if a size is negative. Prefer this over compute_numel() + * for paths that can propagate an Error upward. + */ +::executorch::runtime::Result +safe_numel(const ::executorch::runtime::etensor::TensorImpl::SizesType *sizes, + ssize_t dim); + /// Appropriate format specifier for the result of calling /// size(). Must be used instead of using zd directly to support ATen /// mode. @@ -276,6 +305,7 @@ namespace executor { // TODO(T197294990): Remove these deprecated aliases once all users have moved // to the new `::executorch` namespaces. using ::executorch::runtime::etensor::compute_numel; +using ::executorch::runtime::etensor::safe_numel; using ::executorch::runtime::etensor::TensorImpl; } // namespace executor } // namespace torch diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method.h b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method.h index aecf8c8fa0..dd66951ca5 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method.h @@ -23,6 +23,7 @@ #include #include #include +#include #include // Forward declare flatbuffer types. This is a public header and must not @@ -77,7 +78,7 @@ class Method final { merged_data_map_(std::move(rhs.merged_data_map_)), external_constants_(rhs.external_constants_), n_external_constants_(rhs.n_external_constants_), - init_state_(rhs.init_state_) { + kernel_registry_(rhs.kernel_registry_), init_state_(rhs.init_state_) { // Required: clear out fields that the dtor looks at, so that we don't free // anything twice. rhs.n_value_ = 0; @@ -323,13 +324,15 @@ class Method final { }; Method(const Program *program, MemoryManager *memory_manager, - EventTracer *event_tracer, MemoryAllocator *temp_allocator) + EventTracer *event_tracer, MemoryAllocator *temp_allocator, + Span kernel_registry = {}) : step_state_(), program_(program), memory_manager_(memory_manager), temp_allocator_(temp_allocator), serialization_plan_(nullptr), event_tracer_(event_tracer), n_value_(0), values_(nullptr), input_set_(nullptr), n_delegate_(0), delegates_(nullptr), n_chains_(0), chains_(nullptr), merged_data_map_(nullptr), external_constants_(nullptr), n_external_constants_(0), + kernel_registry_(kernel_registry), init_state_(InitializationState::Uninitialized) {} /// Static factory used by Program. @@ -337,7 +340,8 @@ class Method final { load(executorch_flatbuffer::ExecutionPlan *s_plan, const Program *program, MemoryManager *memory_manager, EventTracer *event_tracer, const NamedDataMap *named_data_map, - const LoadBackendOptionsMap *backend_options = nullptr); + const LoadBackendOptionsMap *backend_options = nullptr, + Span kernel_registry = {}); /** * Initialize the method from its serialized representation. @@ -382,6 +386,8 @@ class Method final { NamedData *external_constants_; size_t n_external_constants_ = 0; + Span kernel_registry_; + InitializationState init_state_; /** diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method_meta.h b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method_meta.h index 3d387c695a..2cacb49506 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method_meta.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/method_meta.h @@ -9,6 +9,7 @@ #pragma once #include +#include #include #include #include @@ -230,6 +231,19 @@ class MethodMeta final { */ Result memory_planned_buffer_size(size_t index) const; + /** + * Get the device placement for the specified memory-planned buffer. + * + * For CPU-only programs (no non_const_buffer_device in the PTE), all buffers + * default to Device{CPU, 0}. For programs with device annotations, returns + * the device type and index that the buffer should be allocated on. + * + * @param[in] index The index of the buffer to look up (0-based, same + * indexing as memory_planned_buffer_size()). + * @returns The Device on success, or an error on failure. + */ + Result memory_planned_buffer_device(size_t index) const; + /** * Check to see if a backend is used in this method. * diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/platform_memory_allocator.h b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/platform_memory_allocator.h index c80d274257..181181b923 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/platform_memory_allocator.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/platform_memory_allocator.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -46,8 +47,17 @@ class PlatformMemoryAllocator final : public MemoryAllocator { return nullptr; } - // Allocate enough memory for the node, the data and the alignment bump. - size_t alloc_size = sizeof(AllocationNode) + size + alignment; + // Check for overflow before computing total allocation size. + // Allocate enough for the node, data, and alignment bump (at most + // alignment - 1 extra bytes to align the data pointer). + size_t alloc_size = 0; + if (c10::add_overflows(sizeof(AllocationNode), size, &alloc_size) || + c10::add_overflows(alloc_size, alignment - 1, &alloc_size)) { + ET_LOG(Error, "Allocation size overflow: size %zu, alignment %zu", size, + alignment); + return nullptr; + } + void *node_memory = runtime::pal_allocate(alloc_size); // If allocation failed, log message and return nullptr. diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/program.h b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/program.h index 59ae346f36..01dbc1fdfa 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/program.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/program.h @@ -21,6 +21,7 @@ #include #include #include +#include #include // Forward declare flatbuffer types. This is a public header and must not @@ -148,7 +149,8 @@ class Program final { load_method(const char *method_name, MemoryManager *memory_manager, EventTracer *event_tracer = nullptr, const NamedDataMap *named_data_map = nullptr, - const LoadBackendOptionsMap *backend_options = nullptr) const; + const LoadBackendOptionsMap *backend_options = nullptr, + Span kernel_registry = {}) const; /** * Gathers metadata for the named method. diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/tensor_parser.h b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/tensor_parser.h index e1fc971d36..9d18e7a351 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/executor/tensor_parser.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/executor/tensor_parser.h @@ -92,8 +92,12 @@ parseListOptionalType(const flatbuffers::Vector *value_indices, ET_CHECK_OR_RETURN_ERROR( index >= 0 && static_cast(index) < values_len, InvalidProgram, "Invalid value index %" PRId32 " for ListOptional", index); + auto optional_result = values[index].tryToOptional(); + if (!optional_result.ok()) { + return optional_result.error(); + } new (&optional_tensor_list[output_idx]) - std::optional(values[index].toOptional()); + std::optional(std::move(optional_result.get())); evalp_list[output_idx] = &values[static_cast(index)]; } output_idx++; diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/kernel/operator_registry.h b/packages/react-native-executorch/third-party/include/executorch/runtime/kernel/operator_registry.h index ddb648d863..bc6c6729d5 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/kernel/operator_registry.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/kernel/operator_registry.h @@ -216,6 +216,15 @@ ::executorch::runtime::Result get_op_function_from_registry(const char *name, Span meta_list = {}); +/** + * Returns the operator with a given name and TensorMeta list from the provided + * kernel list instead of the global registry. + */ +::executorch::runtime::Result +get_op_function_from_registry(const char *name, + Span meta_list, + Span kernel_list); + /** * Returns all registered kernels. */ diff --git a/packages/react-native-executorch/third-party/include/executorch/runtime/platform/profiler.h b/packages/react-native-executorch/third-party/include/executorch/runtime/platform/profiler.h index 6905d64341..9d5671980b 100644 --- a/packages/react-native-executorch/third-party/include/executorch/runtime/platform/profiler.h +++ b/packages/react-native-executorch/third-party/include/executorch/runtime/platform/profiler.h @@ -255,8 +255,7 @@ using ::executorch::runtime::track_allocator; } while (0) #define EXECUTORCH_BEGIN_PROF(name) \ - { \ - } + {} #define EXECUTORCH_END_PROF(token_id) \ do { \ @@ -278,8 +277,7 @@ using ::executorch::runtime::track_allocator; memset(prof_result_test, 0, sizeof(::executorch::runtime::prof_result_t)); #define EXECUTORCH_RESET_PROFILE_RESULTS() \ - { \ - } + {} #define EXECUTORCH_TRACK_ALLOCATOR(name) ((void)(name), -1) diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib index f74ed53c6a..d081c6501b 100755 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist index b2b2aa2478..4b7da1be30 100644 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/mlx.metallib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/mlx.metallib new file mode 100644 index 0000000000..104cc44a31 Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/mlx.metallib differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib index 61193b77ef..160890caab 100755 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist index a6f2d4a5dc..38f26327ea 100644 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/mlx.metallib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/mlx.metallib new file mode 100644 index 0000000000..104cc44a31 Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/mlx.metallib differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj index a4d139dcaa..c51f7098ea 100644 --- a/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj +++ b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 0E4A7F482D67549100D8DCBA /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E4A7F452D67549100D8DCBA /* MetalPerformanceShaders.framework */; }; 0E4A7F492D67549100D8DCBA /* MetalPerformanceShadersGraph.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E4A7F462D67549100D8DCBA /* MetalPerformanceShadersGraph.framework */; }; 5576B4B92CEF970E005027B7 /* ETModel.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5576B4B82CEF970C005027B7 /* ETModel.mm */; }; + A1MLX0001RESBUILD0001 /* mlx.metallib in Resources */ = {isa = PBXBuildFile; fileRef = A1MLX0001FILEREF0001 /* mlx.metallib */; }; 55EA2C572CB90E7D004315B3 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55EA2C562CB90E7D004315B3 /* Accelerate.framework */; }; 55EA2C592CB90E80004315B3 /* CoreML.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55EA2C582CB90E80004315B3 /* CoreML.framework */; }; 55EA2C5B2CB90E85004315B3 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 55EA2C5A2CB90E85004315B3 /* libsqlite3.tbd */; }; @@ -22,6 +23,7 @@ 0E4A7F462D67549100D8DCBA /* MetalPerformanceShadersGraph.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShadersGraph.framework; path = System/Library/Frameworks/MetalPerformanceShadersGraph.framework; sourceTree = SDKROOT; }; 5576B4B62CEF9705005027B7 /* ETModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ETModel.h; sourceTree = ""; }; 5576B4B82CEF970C005027B7 /* ETModel.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ETModel.mm; sourceTree = ""; }; + A1MLX0001FILEREF0001 /* mlx.metallib */ = {isa = PBXFileReference; lastKnownFileType = archive.metal-library; path = mlx.metallib; sourceTree = ""; }; 55EA2C1C2CB90C22004315B3 /* ExecutorchLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ExecutorchLib.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 55EA2C562CB90E7D004315B3 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 55EA2C582CB90E80004315B3 /* CoreML.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreML.framework; path = System/Library/Frameworks/CoreML.framework; sourceTree = SDKROOT; }; @@ -66,6 +68,7 @@ isa = PBXGroup; children = ( 55EA2C352CB90C7A004315B3 /* Exported */, + A1MLX0001FILEREF0001 /* mlx.metallib */, ); path = ExecutorchLib; sourceTree = ""; @@ -153,6 +156,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + A1MLX0001RESBUILD0001 /* mlx.metallib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -343,6 +347,8 @@ "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_ios.a", "-force_load", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mlx_ios.a", + "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_llm_ios.a", @@ -435,6 +441,8 @@ "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_ios.a", "-force_load", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mlx_ios.a", + "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_llm_ios.a", diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib/mlx.metallib b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib/mlx.metallib new file mode 100644 index 0000000000..104cc44a31 Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib/mlx.metallib differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a index 4e2a80a220..796b9d6796 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a index c5b16beb50..8f113a2954 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_ios.a new file mode 100644 index 0000000000..d34e650af7 Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_simulator.a new file mode 100644 index 0000000000..59999e734d Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mlx_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a index ce63bfd904..a8bf9728a9 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a index 3679faaa46..5cce760012 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a index 4049c0a640..2093739b1a 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a index bd145d474f..360569eb71 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a index 221409be07..ebe540bbbf 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a index 40b74e9c7d..a312c92a1e 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a index bec9105acb..8435e5b35a 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a index 99449364f2..e5a58d122d 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a index 5217a54902..ce8352f8d7 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a index 9add6b355c..c8a6cd42a4 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a index 9cc9a42bd2..7b530c9e28 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a index cb06a86712..666ab17b07 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a index d54805247e..2aa82a12d6 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a index 41f06f64fb..567ca5555f 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a index 19db3e80f4..b0d3385e72 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a index af26c633e1..662a2ac890 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a index 8c14cf924b..4d59154358 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a index d6deeb5a00..1ec1dbe74e 100644 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a and b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/mlx.metallib b/packages/react-native-executorch/third-party/ios/libs/executorch/mlx.metallib new file mode 100644 index 0000000000..104cc44a31 Binary files /dev/null and b/packages/react-native-executorch/third-party/ios/libs/executorch/mlx.metallib differ