diff --git a/.ci/docker/ci_commit_pins/pytorch.txt b/.ci/docker/ci_commit_pins/pytorch.txt
index 96724b0411b..8afd359d1ea 100644
--- a/.ci/docker/ci_commit_pins/pytorch.txt
+++ b/.ci/docker/ci_commit_pins/pytorch.txt
@@ -1 +1 @@
-9b498d3bb28b8e3411ce464dd2755c5b96d92c8f
+release/2.8
diff --git a/.ci/docker/common/install_pytorch.sh b/.ci/docker/common/install_pytorch.sh
index 4bf33348681..32f753ba528 100755
--- a/.ci/docker/common/install_pytorch.sh
+++ b/.ci/docker/common/install_pytorch.sh
@@ -32,9 +32,9 @@ install_pytorch_and_domains() {
   pip_install "$(echo dist/*.whl)"
 
   # Grab the pinned audio and vision commits from PyTorch
-  TORCHAUDIO_VERSION=$(cat .github/ci_commit_pins/audio.txt)
+  TORCHAUDIO_VERSION=release/2.8
   export TORCHAUDIO_VERSION
-  TORCHVISION_VERSION=$(cat .github/ci_commit_pins/vision.txt)
+  TORCHVISION_VERSION=release/0.23
   export TORCHVISION_VERSION
 
   install_domains
diff --git a/.ci/scripts/check_c10_sync.sh b/.ci/scripts/check_c10_sync.sh
index 67bc8a3e4c0..809a3a0229b 100755
--- a/.ci/scripts/check_c10_sync.sh
+++ b/.ci/scripts/check_c10_sync.sh
@@ -12,4 +12,4 @@ pushd pytorch
 git checkout "$pytorch_pin"
 popd
 "$(dirname "${BASH_SOURCE[0]}")"/compare_dirs.sh runtime/core/portable_type/c10/c10 pytorch/c10
-"$(dirname "${BASH_SOURCE[0]}")"/compare_dirs.sh runtime/core/portable_type/c10/torch/standalone pytorch/torch/standalone
+"$(dirname "${BASH_SOURCE[0]}")"/compare_dirs.sh runtime/core/portable_type/c10/torch/headeronly pytorch/torch/headeronly
diff --git a/.ci/scripts/setup-linux.sh b/.ci/scripts/setup-linux.sh
index a090571ab49..7edb85e2af7 100755
--- a/.ci/scripts/setup-linux.sh
+++ b/.ci/scripts/setup-linux.sh
@@ -16,12 +16,8 @@ read -r BUILD_TOOL BUILD_MODE EDITABLE < <(parse_args "$@")
 # have already been installed, so we use PyTorch build from source here instead
 # of nightly. This allows CI to test against latest commits from PyTorch
 if [[ "${EDITABLE:-false}" == "true" ]]; then
-  install_executorch --use-pt-pinned-commit --editable
+  install_executorch --editable
 else
-  install_executorch --use-pt-pinned-commit
+  install_executorch
 fi
 build_executorch_runner "${BUILD_TOOL}" "${BUILD_MODE}"
-
-if [[ "${GITHUB_BASE_REF:-}" == *main* || "${GITHUB_BASE_REF:-}" == *gh* ]]; then
-  do_not_use_nightly_on_ci
-fi
diff --git a/.ci/scripts/wheel/post_build_script.sh b/.ci/scripts/wheel/post_build_script.sh
index c785139b3df..1bf20fd9304 100755
--- a/.ci/scripts/wheel/post_build_script.sh
+++ b/.ci/scripts/wheel/post_build_script.sh
@@ -12,3 +12,5 @@ set -eux
 # Rename pip-out directory, to avoid using shared libraries in pip-out during
 # smoke test.
 mv pip-out BACKUP-pip-out
+
+pip list
diff --git a/.ci/scripts/wheel/pre_build_script.sh b/.ci/scripts/wheel/pre_build_script.sh
index 424529af864..4c08b4a907c 100755
--- a/.ci/scripts/wheel/pre_build_script.sh
+++ b/.ci/scripts/wheel/pre_build_script.sh
@@ -15,3 +15,5 @@ set -euxo pipefail
 # able to see the installed torch package.
 
 "${GITHUB_WORKSPACE}/${REPOSITORY}/install_requirements.sh"  --example
+
+pip list
diff --git a/.ci/scripts/wheel/test_base.py b/.ci/scripts/wheel/test_base.py
index f8a7309a6c2..278e46fe75a 100644
--- a/.ci/scripts/wheel/test_base.py
+++ b/.ci/scripts/wheel/test_base.py
@@ -41,6 +41,18 @@ class ModelTest:
 
 
 def run_tests(model_tests: List[ModelTest]) -> None:
+    # Test that we can import the portable_lib module - verifies RPATH is correct
+    print("Testing portable_lib import...")
+    try:
+        from executorch.extension.pybindings._portable_lib import (  # noqa: F401
+            _load_for_executorch,
+        )
+
+        print("✓ Successfully imported _load_for_executorch from portable_lib")
+    except ImportError as e:
+        print(f"✗ Failed to import portable_lib: {e}")
+        raise
+
     # Why are we doing this envvar shenanigans? Since we build the testers, which
     # uses buck, we cannot run as root. This is a sneaky of getting around that
     # test.
diff --git a/.github/workflows/_android.yml b/.github/workflows/_android.yml
index 630ae2747bf..477a46c5c08 100644
--- a/.github/workflows/_android.yml
+++ b/.github/workflows/_android.yml
@@ -7,7 +7,7 @@ on:
 jobs:
   build-llm-demo:
     name: build-llm-demo
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -71,7 +71,7 @@ jobs:
       API_LEVEL: 34
     steps:
       - name: Setup SSH (Click me for login details)
-        uses: pytorch/test-infra/.github/actions/setup-ssh@main
+        uses: pytorch/test-infra/.github/actions/setup-ssh@release/2.8
         with:
           github-secret: ${{ secrets.GITHUB_TOKEN }}
           instructions: |
@@ -82,7 +82,7 @@ jobs:
           submodules: false
 
       - name: Setup conda
-        uses: pytorch/test-infra/.github/actions/setup-miniconda@main
+        uses: pytorch/test-infra/.github/actions/setup-miniconda@release/2.8
         with:
           python-version: '3.10'
 
diff --git a/.github/workflows/_link_check.yml b/.github/workflows/_link_check.yml
index 2e96b0fd118..bd16d5b1e7a 100644
--- a/.github/workflows/_link_check.yml
+++ b/.github/workflows/_link_check.yml
@@ -8,7 +8,7 @@ on:
 jobs:
   lint-urls:
     if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'skip-url-lint') }}
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     with:
       runner: linux.2xlarge
       docker-image: executorch-ubuntu-22.04-linter
@@ -33,7 +33,7 @@ jobs:
 
   lint-xrefs:
     if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'skip-xref-lint') }}
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     with:
       runner: linux.2xlarge
       docker-image: executorch-ubuntu-22.04-linter
diff --git a/.github/workflows/_unittest.yml b/.github/workflows/_unittest.yml
index 63f5e6693b7..577c858865b 100644
--- a/.github/workflows/_unittest.yml
+++ b/.github/workflows/_unittest.yml
@@ -26,7 +26,7 @@ on:
 
 jobs:
   linux:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -41,7 +41,7 @@ jobs:
         .ci/scripts/unittest-linux.sh --build-tool "${{ inputs.build-tool }}" --build-mode "${{ inputs.build-mode }}" --editable "${{ inputs.editable }}"
 
   macos:
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     with:
       runner: macos-m1-stable
       python-version: '3.11'
diff --git a/.github/workflows/android-perf.yml b/.github/workflows/android-perf.yml
index eba12967e5f..ace10a87cc6 100644
--- a/.github/workflows/android-perf.yml
+++ b/.github/workflows/android-perf.yml
@@ -157,7 +157,7 @@ jobs:
 
   export-models:
     name: export-models
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -402,7 +402,7 @@ jobs:
 
   build-benchmark-app:
     name: build-benchmark-app
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -450,7 +450,7 @@ jobs:
     permissions:
       id-token: write
       contents: read
-    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@release/2.8
     needs:
       - set-parameters
       - prepare-test-specs
@@ -496,7 +496,7 @@ jobs:
           aws-region: us-east-1
 
       - name: Setup conda
-        uses: pytorch/test-infra/.github/actions/setup-miniconda@main
+        uses: pytorch/test-infra/.github/actions/setup-miniconda@release/2.8
         with:
           python-version: '3.10'
 
@@ -554,7 +554,7 @@ jobs:
           done
 
       - name: Upload the benchmark results (v3)
-        uses: pytorch/test-infra/.github/actions/upload-benchmark-results@main
+        uses: pytorch/test-infra/.github/actions/upload-benchmark-results@release/2.8
         with:
           benchmark-results-dir: benchmark-results/v3
           dry-run: false
diff --git a/.github/workflows/android-release-artifacts.yml b/.github/workflows/android-release-artifacts.yml
index b31ff644d94..0e6879bd416 100644
--- a/.github/workflows/android-release-artifacts.yml
+++ b/.github/workflows/android-release-artifacts.yml
@@ -47,15 +47,13 @@ jobs:
     name: build-aar
     needs: check-if-aar-exists
     if: ${{ !github.event.pull_request.head.repo.fork }}
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.7
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
     secrets: inherit
     permissions:
       id-token: write
       contents: read
     with:
       secrets-env: EXECUTORCH_MAVEN_SIGNING_KEYID EXECUTORCH_MAVEN_SIGNING_PASSWORD EXECUTORCH_MAVEN_CENTRAL_PASSWORD EXECUTORCH_MAVEN_CENTRAL_USERNAME EXECUTORCH_MAVEN_SIGNING_GPG_KEY_CONTENTS
-      # As this job has access to Maven credential, run this on a fresh ephemeral runner
-      runner: ephemeral.linux.2xlarge
       docker-image: executorch-ubuntu-22.04-clang12-android
       submodules: 'recursive'
       ref: ${{ github.sha }}
diff --git a/.github/workflows/apple-perf.yml b/.github/workflows/apple-perf.yml
index 575000f5bc0..2226f7c07a5 100644
--- a/.github/workflows/apple-perf.yml
+++ b/.github/workflows/apple-perf.yml
@@ -159,7 +159,7 @@ jobs:
 
   export-models:
     name: export-models
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     needs: set-parameters
     secrets: inherit
     strategy:
@@ -411,7 +411,7 @@ jobs:
 
   build-benchmark-app:
     name: build-benchmark-app
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     needs:
       - set-parameters
     secrets: inherit
@@ -496,7 +496,7 @@ jobs:
     permissions:
       id-token: write
       contents: read
-    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@release/2.8
     strategy:
       matrix: ${{ fromJson(needs.set-parameters.outputs.benchmark_configs) }}
       fail-fast: false
@@ -539,7 +539,7 @@ jobs:
           aws-region: us-east-1
 
       - name: Setup conda
-        uses: pytorch/test-infra/.github/actions/setup-miniconda@main
+        uses: pytorch/test-infra/.github/actions/setup-miniconda@release/2.8
         with:
           python-version: '3.10'
 
@@ -595,7 +595,7 @@ jobs:
           done
 
       - name: Upload the benchmark results (v3)
-        uses: pytorch/test-infra/.github/actions/upload-benchmark-results@main
+        uses: pytorch/test-infra/.github/actions/upload-benchmark-results@release/2.8
         with:
           benchmark-results-dir: benchmark-results/v3
           dry-run: false
diff --git a/.github/workflows/apple.yml b/.github/workflows/apple.yml
index dcd4a0ab2a3..c84112d4662 100644
--- a/.github/workflows/apple.yml
+++ b/.github/workflows/apple.yml
@@ -39,14 +39,14 @@ jobs:
         id: set_version
         shell: bash
         run: |
-          VERSION="0.7.0.$(TZ='PST8PDT' date +%Y%m%d)"
+          VERSION="0.7.0"
           echo "version=$VERSION" >> "$GITHUB_OUTPUT"
 
   build-demo-ios:
     name: build-demo-ios
     # NB: Don't run this on fork PRs because they won't have access to the secret and would fail anyway
     if: ${{ !github.event.pull_request.head.repo.fork }}
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     secrets: inherit
     with:
       runner: macos-latest-xlarge
@@ -116,7 +116,7 @@ jobs:
     permissions:
       id-token: write
       contents: read
-    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/mobile_job.yml@release/2.8
     with:
       device-type: ios
       # For iOS testing, the runner just needs to call AWS Device Farm, so there is no need to run this on macOS
@@ -134,7 +134,7 @@ jobs:
   build-frameworks-ios:
     name: build-frameworks-ios
     needs: set-version
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     with:
       runner: macos-latest-xlarge
       python-version: '3.11'
@@ -184,7 +184,7 @@ jobs:
     runs-on: ubuntu-22.04
     needs: [build-frameworks-ios, set-version]
     timeout-minutes: 30
-    environment: ${{ github.ref == 'refs/heads/main' && 'cherry-pick-bot' || '' }}
+    environment: ${{ github.ref == 'refs/heads/release/0.7' && 'cherry-pick-bot' || '' }}
     permissions:
       id-token: write
       contents: write
@@ -209,7 +209,7 @@ jobs:
           name: executorch-frameworks-ios
           path: ${{ runner.temp }}/frameworks-ios/
       - name: Only push to S3 when running the workflow manually from main branch
-        if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/main' }}
+        if: ${{ (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.ref == 'refs/heads/release/0.7' }}
         shell: bash
         run: |
           echo "UPLOAD_ON_MAIN=1" >> "${GITHUB_ENV}"
@@ -271,7 +271,7 @@ jobs:
     name: build-benchmark-app
     # NB: Don't run this on fork PRs because they won't have access to the secret and would fail anyway
     if: ${{ !github.event.pull_request.head.repo.fork }}
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     secrets: inherit
     with:
       runner: macos-latest-xlarge
diff --git a/.github/workflows/build-presets.yml b/.github/workflows/build-presets.yml
index 1a40c6ef68d..46e821d6233 100644
--- a/.github/workflows/build-presets.yml
+++ b/.github/workflows/build-presets.yml
@@ -16,7 +16,7 @@ concurrency:
 
 jobs:
   apple:
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       fail-fast: false
       matrix:
@@ -35,7 +35,7 @@ jobs:
         ${CONDA_RUN} cmake --build cmake-out -j$(( $(sysctl -n hw.ncpu) - 1 ))
 
   linux:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     strategy:
       fail-fast: false
       matrix:
@@ -68,7 +68,7 @@ jobs:
         cmake --build cmake-out -j$(( $(nproc) - 1 ))
 
   windows:
-    uses: pytorch/test-infra/.github/workflows/windows_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/windows_job.yml@release/2.8
     strategy:
       fail-fast: false
       matrix:
diff --git a/.github/workflows/build-wheels-linux.yml b/.github/workflows/build-wheels-linux.yml
index 4c08968f3d6..5ee0e862d39 100644
--- a/.github/workflows/build-wheels-linux.yml
+++ b/.github/workflows/build-wheels-linux.yml
@@ -22,7 +22,7 @@ on:
 
 jobs:
   generate-matrix:
-    uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
+    uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@release/2.8
     with:
       package-type: wheel
       os: linux
@@ -47,7 +47,7 @@ jobs:
             smoke-test-script: .ci/scripts/wheel/test_linux.py
             package-name: executorch
     name: ${{ matrix.repository }}
-    uses: pytorch/test-infra/.github/workflows/build_wheels_linux.yml@main
+    uses: pytorch/test-infra/.github/workflows/build_wheels_linux.yml@release/2.8
     with:
       repository: ${{ matrix.repository }}
       ref: ""
diff --git a/.github/workflows/build-wheels-macos.yml b/.github/workflows/build-wheels-macos.yml
index 3a394cff64b..95dcec390b8 100644
--- a/.github/workflows/build-wheels-macos.yml
+++ b/.github/workflows/build-wheels-macos.yml
@@ -22,7 +22,7 @@ on:
 
 jobs:
   generate-matrix:
-    uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
+    uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@release/2.8
     with:
       package-type: wheel
       os: macos-arm64
@@ -47,7 +47,7 @@ jobs:
             smoke-test-script: .ci/scripts/wheel/test_macos.py
             package-name: executorch
     name: ${{ matrix.repository }}
-    uses: pytorch/test-infra/.github/workflows/build_wheels_macos.yml@main
+    uses: pytorch/test-infra/.github/workflows/build_wheels_macos.yml@release/2.8
     with:
       repository: ${{ matrix.repository }}
       ref: ""
diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml
index 8e5519d33ef..300adbd8349 100644
--- a/.github/workflows/doc-build.yml
+++ b/.github/workflows/doc-build.yml
@@ -15,7 +15,7 @@ on:
 
 jobs:
   build:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -93,7 +93,7 @@ jobs:
     permissions:
       id-token: write
       contents: write
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     with:
       repository: pytorch/executorch
       download-artifact: docs
diff --git a/.github/workflows/docker-builds.yml b/.github/workflows/docker-builds.yml
index 791a52b96c1..5a38c26d7cb 100644
--- a/.github/workflows/docker-builds.yml
+++ b/.github/workflows/docker-builds.yml
@@ -57,7 +57,7 @@ jobs:
           mkdir "${GITHUB_WORKSPACE}"
 
       - name: Setup SSH (Click me for login details)
-        uses: pytorch/test-infra/.github/actions/setup-ssh@main
+        uses: pytorch/test-infra/.github/actions/setup-ssh@release/2.8
         with:
           github-secret: ${{ secrets.GITHUB_TOKEN }}
 
@@ -65,11 +65,11 @@ jobs:
         uses: actions/checkout@v3
 
       - name: Setup Linux
-        uses: pytorch/test-infra/.github/actions/setup-linux@main
+        uses: pytorch/test-infra/.github/actions/setup-linux@release/2.8
 
       - name: Build docker image
         id: build-docker-image
-        uses: pytorch/test-infra/.github/actions/calculate-docker-image@main
+        uses: pytorch/test-infra/.github/actions/calculate-docker-image@release/2.8
         with:
           docker-image-name: ${{ matrix.docker-image-name }}
           always-rebuild: true
@@ -77,5 +77,5 @@ jobs:
           force-push: true
 
       - name: Teardown Linux
-        uses: pytorch/test-infra/.github/actions/teardown-linux@main
+        uses: pytorch/test-infra/.github/actions/teardown-linux@release/2.8
         if: always()
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index f4f0c91c4ad..4952d9bdea2 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -16,7 +16,7 @@ concurrency:
 
 jobs:
   lintrunner:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -70,7 +70,7 @@ jobs:
       ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
 
   android-java-format:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 4658fdc0d26..b9b57e66b81 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -21,7 +21,7 @@ jobs:
     environment: ${{ (github.event_name == 'schedule') && 'update-commit-hash' || '' }}
     steps:
       - name: update-pytorch-commit-hash
-        uses: pytorch/test-infra/.github/actions/update-commit-hash@main
+        uses: pytorch/test-infra/.github/actions/update-commit-hash@release/2.8
         if: ${{ github.event_name == 'schedule' }}
         with:
           repo-name: pytorch
diff --git a/.github/workflows/periodic.yml b/.github/workflows/periodic.yml
index 59b3a522954..382c979c4fd 100644
--- a/.github/workflows/periodic.yml
+++ b/.github/workflows/periodic.yml
@@ -39,7 +39,7 @@ jobs:
 
   test-models-linux:
     name: test-models-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
diff --git a/.github/workflows/pull.yml b/.github/workflows/pull.yml
index df254b7f409..d3b02f8237c 100644
--- a/.github/workflows/pull.yml
+++ b/.github/workflows/pull.yml
@@ -15,7 +15,7 @@ concurrency:
 jobs:
   test-setup-linux-gcc:
     name: test-setup-linux-gcc
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -40,7 +40,7 @@ jobs:
 
   test-models-linux-basic:
     name: test-models-linux-basic
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -84,7 +84,7 @@ jobs:
 
   test-models-linux:
     name: test-models-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -138,7 +138,7 @@ jobs:
   test-llama-runner-linux:
     # Test Both linux x86 and linux aarch64
     name: test-llama-runner-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -188,7 +188,7 @@ jobs:
 
   test-llama-runner-linux-android:
     name: test-llama-runner-linux-android
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -213,7 +213,7 @@ jobs:
 
   test-custom-ops-linux:
     name: test-custom-ops-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -237,7 +237,7 @@ jobs:
 
   test-selective-build-linux:
     name: test-selective-build-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -261,7 +261,7 @@ jobs:
 
   test-llava-runner-linux:
     name: test-llava-runner-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -292,7 +292,7 @@ jobs:
 
   test-moshi-linux:
     name: test-moshi-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -322,7 +322,7 @@ jobs:
 
   test-quantized-aot-lib-linux:
     name: test-quantized-aot-lib-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -345,7 +345,7 @@ jobs:
 
   test-binary-size-linux-gcc:
     name: test-binary-size-linux-gcc
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -381,7 +381,7 @@ jobs:
 
   test-binary-size-linux:
     name: test-binary-size-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -453,7 +453,7 @@ jobs:
 
   unittest-arm-backend-with-no-fvp:
     name: unittest-arm-backend-with-no-fvp
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -486,7 +486,7 @@ jobs:
 
   test-llama-runner-qnn-linux:
     name: test-llama-runner-qnn-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -525,7 +525,7 @@ jobs:
 
   test-static-llama-qnn-linux:
     name: test-static-llama-qnn-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -558,7 +558,7 @@ jobs:
 
   test-qnn-models-linux:
     name: test-qnn-models-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -580,7 +580,7 @@ jobs:
 
   test-phi-3-mini-runner-linux:
     name: test-phi-3-mini-runner-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -607,7 +607,7 @@ jobs:
 
   test-eval_llama-wikitext-linux:
     name: test-eval_llama-wikitext-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -634,7 +634,7 @@ jobs:
 
   test-eval_llama-mmlu-linux:
     name: test-eval_llama-mmlu-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -661,7 +661,7 @@ jobs:
 
   test-llama_runner_eager-linux:
     name: test-llama_runner_eager-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -688,7 +688,7 @@ jobs:
 
   test-mediatek-models-linux:
     name: test-mediatek-models-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -706,16 +706,19 @@ jobs:
         conda activate "${CONDA_ENV}"
 
         source .ci/scripts/utils.sh
-        install_executorch "--use-pt-pinned-commit"
 
+        # Run this first so that torch won't be overriden
         PYTHON_EXECUTABLE=python bash .ci/scripts/setup-mediatek-deps.sh
+        install_executorch
+        pip list
+        # debug
         PYTHON_EXECUTABLE=python bash .ci/scripts/build-mediatek-sdk.sh
         PYTHON_EXECUTABLE=python bash .ci/scripts/test_model.sh "mv3" "buck2" "mediatek"
         # placeholder for mediatek to add more tests
 
   test-openvino-linux:
     name: test-openvino-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
diff --git a/.github/workflows/trunk.yml b/.github/workflows/trunk.yml
index 0eceddca36f..e07cbc1dafd 100644
--- a/.github/workflows/trunk.yml
+++ b/.github/workflows/trunk.yml
@@ -20,7 +20,7 @@ concurrency:
 jobs:
   test-models-macos:
     name: test-models-macos
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       matrix:
         # Mac runners are expensive and limited, and non reliable.
@@ -57,7 +57,7 @@ jobs:
 
   test-models-linux-aarch64:
     name: test-models-linux-aarch64
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -107,7 +107,7 @@ jobs:
 
   test-custom-ops-macos:
     name: test-custom-ops-macos
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       matrix:
         include:
@@ -129,7 +129,7 @@ jobs:
 
   test-selective-build-macos:
     name: test-selective-build-macos
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       matrix:
         include:
@@ -151,7 +151,7 @@ jobs:
 
   test-demo-backend-delegation:
     name: test-demo-backend-delegation
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -178,7 +178,7 @@ jobs:
 
   test-arm-backend:
     name: test-arm-backend
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -219,7 +219,7 @@ jobs:
 
   test-arm-cortex-m-size-test:
     name: test-arm-cortex-m-size-test
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -273,7 +273,7 @@ jobs:
 
   test-coreml-delegate:
     name: test-coreml-delegate
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     with:
       runner: macos-latest-xlarge
       python-version: '3.11'
@@ -291,7 +291,7 @@ jobs:
 
   test-static-llama-ane:
     name: test-static-llama-ane
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     with:
       runner: macos-m1-stable
       python-version: '3.11'
@@ -313,7 +313,7 @@ jobs:
 
   test-llama-torchao-lowbit:
     name: test-llama-torchao-lowbit
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     with:
       runner: macos-m1-stable
       python-version: '3.11'
@@ -325,7 +325,7 @@ jobs:
         eval "$(conda shell.bash hook)"
 
         # Install requirements
-        ${CONDA_RUN} python install_executorch.py
+        ${CONDA_RUN} EXECUTORCH_BUILD_TORCHAO=1 python install_executorch.py
         ${CONDA_RUN} sh examples/models/llama/install_requirements.sh
 
         # Run test
@@ -334,7 +334,7 @@ jobs:
   test-llama-runner-linux:
     # Test Both linux x86 and linux aarch64
     name: test-llama-runner-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -392,7 +392,7 @@ jobs:
 
   test-llama-runner-macos:
     name: test-llama-runner-mac
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       matrix:
         dtype: [fp32]
@@ -428,7 +428,7 @@ jobs:
   # # TODO(jackzhxng): Runner consistently runs out of memory before test finishes. Try to find a more powerful runner.
   # test-llava-runner-macos:
   #   name: test-llava-runner-macos
-  #   uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+  #   uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
   #   strategy:
   #     fail-fast: false
   #   with:
@@ -456,7 +456,7 @@ jobs:
 
   test-qnn-model:
     name: test-qnn-model
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -482,7 +482,7 @@ jobs:
 
   test-qnn-optimum-model:
     name: test-qnn-optimum-model
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -508,7 +508,7 @@ jobs:
 
   test-apple-model:
     name: test-apple-model
-    uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
+    uses: pytorch/test-infra/.github/workflows/macos_job.yml@release/2.8
     strategy:
       fail-fast: false
     with:
@@ -543,7 +543,7 @@ jobs:
     # NB: Don't run this on fork PRs because they won't have access to the secret and would fail anyway
     if: ${{ !github.event.pull_request.head.repo.fork }}
     name: test-huggingface-transformers
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -664,7 +664,7 @@ jobs:
 
   test-llama-runner-qnn-linux:
     name: test-llama-runner-qnn-linux
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
@@ -712,7 +712,7 @@ jobs:
       docker-image: executorch-ubuntu-22.04-clang12
 
   unittest-nxp-neutron:
-    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
+    uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@release/2.8
     permissions:
       id-token: write
       contents: read
diff --git a/.github/workflows/update-viablestrict.yml b/.github/workflows/update-viablestrict.yml
index e639c497549..b72acfaa73c 100644
--- a/.github/workflows/update-viablestrict.yml
+++ b/.github/workflows/update-viablestrict.yml
@@ -16,7 +16,7 @@ jobs:
     environment: ${{ (github.event_name == 'schedule') && 'update-viable-strict' || '' }}
     steps:
       - name: Update viable/strict
-        uses: pytorch/test-infra/.github/actions/update-viablestrict@main
+        uses: pytorch/test-infra/.github/actions/update-viablestrict@release/2.8
         with:
           repository: pytorch/executorch
           stable-branch: viable/strict
diff --git a/.gitmodules b/.gitmodules
index 702bf091f21..945ae5ed51e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -27,7 +27,7 @@
 	url = https://github.com/pytorch/cpuinfo.git
 [submodule "backends/xnnpack/third-party/pthreadpool"]
 	path = backends/xnnpack/third-party/pthreadpool
-	url = https://github.com/Maratyszcza/pthreadpool.git
+	url = https://github.com/google/pthreadpool.git
 [submodule "extension/llm/tokenizers"]
 	path = extension/llm/tokenizers
 	url = https://github.com/pytorch-labs/tokenizers.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8977e5c5aa9..57afd265446 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,7 +45,9 @@
 # ~~~
 #
 
-cmake_minimum_required(VERSION 3.24)
+# TODO Lower to 3.24 when XNNPACK dependency is updated to include
+# https://github.com/google/XNNPACK/commit/c690daa67f883e1b627aadf7684c06797e9a0684
+cmake_minimum_required(VERSION 3.29)
 project(executorch)
 
 # MARK: - Start EXECUTORCH_H12025_BUILD_MIGRATION
@@ -652,6 +654,20 @@ if(EXECUTORCH_BUILD_PYBIND)
   target_compile_options(portable_lib PUBLIC ${_pybind_compile_options})
   target_link_libraries(portable_lib PRIVATE ${_dep_libs})
 
+  # Set RPATH to find PyTorch libraries relative to the installation location
+  # This goes from executorch/extension/pybindings up to site-packages, then to torch/lib
+  if(APPLE)
+    set_target_properties(portable_lib PROPERTIES
+      BUILD_RPATH "@loader_path/../../../torch/lib"
+      INSTALL_RPATH "@loader_path/../../../torch/lib"
+    )
+  else()
+    set_target_properties(portable_lib PROPERTIES
+      BUILD_RPATH "$ORIGIN/../../../torch/lib"
+      INSTALL_RPATH "$ORIGIN/../../../torch/lib"
+    )
+  endif()
+
   install(TARGETS portable_lib
           LIBRARY DESTINATION executorch/extension/pybindings
   )
diff --git a/README.md b/README.md
index 8003b25b17b..4ddc84ee253 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ Platform Support:
   - Arm
   - Cadence
   - MediaTek
+  - NXP
   - OpenVINO
   - Qualcomm
   - Vulkan
diff --git a/backends/xnnpack/cmake/Dependencies.cmake b/backends/xnnpack/cmake/Dependencies.cmake
index ca31a1e45cb..71ade68ccb2 100644
--- a/backends/xnnpack/cmake/Dependencies.cmake
+++ b/backends/xnnpack/cmake/Dependencies.cmake
@@ -68,10 +68,12 @@ install(TARGETS xnnpack-microkernels-prod
 
 
 if(EXECUTORCH_XNNPACK_ENABLE_KLEIDI)
-    install(TARGETS kleidiai
-        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+    if(TARGET kleidiai)
+        install(TARGETS kleidiai
+            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+            PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+    endif()
 endif()
 
 # Revert PIC Flag to what it originally was
diff --git a/backends/xnnpack/quantizer/xnnpack_quantizer_utils.py b/backends/xnnpack/quantizer/xnnpack_quantizer_utils.py
index 3d687d0b513..e78cb5aca90 100644
--- a/backends/xnnpack/quantizer/xnnpack_quantizer_utils.py
+++ b/backends/xnnpack/quantizer/xnnpack_quantizer_utils.py
@@ -271,7 +271,6 @@ def _do_annotate_conv(
 
         # skip if transposed conv has more than 1 group
         skip = skip or (is_conv_transpose and num_groups != 1)
-        print(f"{skip} conv transpose and num_groups")
 
         if is_conv_transpose:
             # transposed convs per output channel quantization
diff --git a/backends/xnnpack/test/ops/test_exp.py b/backends/xnnpack/test/ops/test_exp.py
index 8646a26cc62..2b51c5b0e47 100644
--- a/backends/xnnpack/test/ops/test_exp.py
+++ b/backends/xnnpack/test/ops/test_exp.py
@@ -34,6 +34,9 @@ def run_exp_test(self, inputs):
             .run_method_and_compare_outputs()
         )
 
+    # TODO (leafs1): Fix flaky tests. Land fix asap
+    # and cherry-pick onto release/0.7 branch
+    @unittest.skip(reason="For float16, numerical discepancies are too high")
     def test_fp16_exp(self):
         inputs = (torch.randn(20).to(torch.float16),)
         self.run_exp_test(inputs)
diff --git a/backends/xnnpack/test/quantizer/test_pt2e_quantization.py b/backends/xnnpack/test/quantizer/test_pt2e_quantization.py
index f97236bed7b..5d76ecd2d54 100644
--- a/backends/xnnpack/test/quantizer/test_pt2e_quantization.py
+++ b/backends/xnnpack/test/quantizer/test_pt2e_quantization.py
@@ -6,10 +6,8 @@
 
 # pyre-unsafe
 
-import unittest
-
 from collections import Counter
-from typing import Dict, Tuple
+from typing import Tuple
 
 import torch
 from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
@@ -33,19 +31,15 @@
 from torch.testing._internal.common_utils import (
     instantiate_parametrized_tests,
     TemporaryFileName,
-    TestCase,
 )
 from torchao.quantization.pt2e import (
     allow_exported_model_train_eval,
     compare_results,
-    CUSTOM_KEY,
     extract_results_from_loggers,
-    generate_numeric_debug_handle,
-    NUMERIC_DEBUG_HANDLE_KEY,
+    FROM_NODE_KEY,
     prepare_for_propagation_comparison,
 )
 
-from torchao.quantization.pt2e.graph_utils import bfs_trace_with_node_process
 from torchao.quantization.pt2e.quantize_pt2e import (
     convert_pt2e,
     prepare_pt2e,
@@ -53,7 +47,10 @@
 )
 from torchao.quantization.pt2e.quantizer import ComposableQuantizer, Quantizer
 from torchao.quantization.pt2e.quantizer.embedding_quantizer import EmbeddingQuantizer
-from torchao.testing.pt2e.utils import PT2EQuantizationTestCase
+from torchao.testing.pt2e.utils import (
+    PT2ENumericDebuggerTestCase,
+    PT2EQuantizationTestCase,
+)
 
 
 class TestQuantizePT2E(PT2EQuantizationTestCase):
@@ -495,7 +492,8 @@ def forward(self, x):
         for n in m.graph.nodes:
             if n.op == "get_attr" and "frozen_param" in n.target:
                 for key in n.meta:
-                    self.assertEqual(n.meta[key], weight_meta[key])
+                    if key != FROM_NODE_KEY:
+                        self.assertEqual(n.meta[key], weight_meta[key])
 
     def test_reentrant(self) -> None:
         """Test we can safely call quantization apis multiple times"""
@@ -725,76 +723,59 @@ def test_save_load(self) -> None:
 instantiate_parametrized_tests(TestQuantizePT2E)
 
 
-@unittest.skip("TODO: Reenable it after debug infrature finish update")
-class TestNumericDebugger(TestCase):
-    def _extract_debug_handles(self, model) -> Dict[str, int]:
-        debug_handle_map: Dict[str, int] = {}
-
-        def _extract_debug_handles_from_node(node: torch.fx.Node) -> None:
-            nonlocal debug_handle_map
-            if (
-                CUSTOM_KEY in node.meta
-                and NUMERIC_DEBUG_HANDLE_KEY in node.meta[CUSTOM_KEY]
-            ):
-                debug_handle_map[str(node)] = node.meta[CUSTOM_KEY][
-                    NUMERIC_DEBUG_HANDLE_KEY
-                ]
-
-        bfs_trace_with_node_process(model, _extract_debug_handles_from_node)
-        return debug_handle_map
-
-    def _assert_each_node_has_debug_handle(self, model) -> None:
-        def _assert_node_has_debug_handle(node: torch.fx.Node) -> None:
-            self.assertTrue(
-                CUSTOM_KEY in node.meta
-                and NUMERIC_DEBUG_HANDLE_KEY in node.meta[CUSTOM_KEY],
-                f"Node {node} doesn't have debug handle",
-            )
-
-        bfs_trace_with_node_process(model, _assert_node_has_debug_handle)
+class TestXNNPACKQuantizerNumericDebugger(PT2ENumericDebuggerTestCase):
 
-    def test_quantize_pt2e_preserve_handle(self) -> None:
+    def test_quantize_pt2e_preserve_handle(self):
         m = TestHelperModules.Conv2dThenConv1d()
         example_inputs = m.example_inputs()
         ep = export_for_training(m, example_inputs, strict=True)
-        generate_numeric_debug_handle(ep)
         m = ep.module()
 
         quantizer = XNNPACKQuantizer().set_global(
             get_symmetric_quantization_config(is_per_channel=False)
         )
-        m = prepare_pt2e(m, quantizer)  # pyre-ignore[6]
-        debug_handle_map = self._extract_debug_handles(m)
-        res_counter = Counter(debug_handle_map.values())
-        repeated_debug_handle_ids = [1, 2, 3]
-        # 3 ids were repeated because we copy over the id from node to its output observer
+        m = prepare_pt2e(m, quantizer)
+        from_node_source_map = self._extract_from_node_source(m)
+        node_name_equip_with_output_observer = [
+            "conv2d",
+            "conv1d",
+            "squeeze",
+        ]
+        res_counter = Counter(from_node_source_map.values())
+        repeated_from_node_source = [
+            from_node_source_map[n_name]
+            for n_name in node_name_equip_with_output_observer
+        ]
+        # 3 infos were repeated because we copy over the info from node to its output observer
         # torch.ops.aten.conv2d.default, torch.ops.aten.squeeze.dim and torch.ops.aten.conv1d.default
-        for dh_id in repeated_debug_handle_ids:
-            self.assertEqual(res_counter[dh_id], 2)
+        for from_node_source in repeated_from_node_source:
+            self.assertEqual(res_counter[from_node_source], 2)
 
         m(*example_inputs)
         m = convert_pt2e(m)
-        self._assert_each_node_has_debug_handle(ep)
-        debug_handle_map = self._extract_debug_handles(m)
-        res_counter = Counter(debug_handle_map.values())
-        # same set of ids where repeated, because we copy over the id from observer/fake_quant to
-        # dequantize node
-        repeated_debug_handle_ids = [1, 2, 3]
-        for dh_id in repeated_debug_handle_ids:
-            self.assertEqual(res_counter[dh_id], 2)
-
-    def test_extract_results_from_loggers(self) -> None:
+        self._assert_each_node_has_from_node_source(m)
+        from_node_source_map = self._extract_from_node_source(m)
+        res_counter = Counter(from_node_source_map.values())
+        # same set of infos where repeated, because we copy over the info from observer/fake_quant to
+        # quantize/dequantize node
+        repeated_from_node_source = [
+            from_node_source_map[n_name]
+            for n_name in node_name_equip_with_output_observer
+        ]
+        for from_node_source in repeated_from_node_source:
+            self.assertEqual(res_counter[from_node_source], 3)
+
+    def test_extract_results_from_loggers(self):
         m = TestHelperModules.Conv2dThenConv1d()
         example_inputs = m.example_inputs()
         ep = export_for_training(m, example_inputs, strict=True)
-        generate_numeric_debug_handle(ep)
         m = ep.module()
-        m_ref_logger = prepare_for_propagation_comparison(m)  # pyre-ignore[6]
+        m_ref_logger = prepare_for_propagation_comparison(m)
 
         quantizer = XNNPACKQuantizer().set_global(
             get_symmetric_quantization_config(is_per_channel=False)
         )
-        m = prepare_pt2e(m, quantizer)  # pyre-ignore[6]
+        m = prepare_pt2e(m, quantizer)
         m(*example_inputs)
         m = convert_pt2e(m)
         m_quant_logger = prepare_for_propagation_comparison(m)
@@ -803,29 +784,22 @@ def test_extract_results_from_loggers(self) -> None:
         m_quant_logger(*example_inputs)
         ref_results = extract_results_from_loggers(m_ref_logger)
         quant_results = extract_results_from_loggers(m_quant_logger)
-        comparison_results = compare_results(
-            ref_results,
-            quant_results,  # pyre-ignore[6]
-        )
+        comparison_results = compare_results(ref_results, quant_results)
         for node_summary in comparison_results.values():
             if len(node_summary.results) > 0:
-                self.assertGreaterEqual(
-                    node_summary.results[0].sqnr,
-                    35,  # pyre-ignore[6]
-                )
+                self.assertGreaterEqual(node_summary.results[0].sqnr, 35)
 
-    def test_extract_results_from_loggers_list_output(self) -> None:
+    def test_extract_results_from_loggers_list_output(self):
         m = TestHelperModules.Conv2dWithSplit()
         example_inputs = m.example_inputs()
         ep = export_for_training(m, example_inputs, strict=True)
-        generate_numeric_debug_handle(ep)
         m = ep.module()
-        m_ref_logger = prepare_for_propagation_comparison(m)  # pyre-ignore[6]
+        m_ref_logger = prepare_for_propagation_comparison(m)
 
         quantizer = XNNPACKQuantizer().set_global(
             get_symmetric_quantization_config(is_per_channel=False)
         )
-        m = prepare_pt2e(m, quantizer)  # pyre-ignore[6]
+        m = prepare_pt2e(m, quantizer)
         m(*example_inputs)
         m = convert_pt2e(m)
         m_quant_logger = prepare_for_propagation_comparison(m)
@@ -834,10 +808,7 @@ def test_extract_results_from_loggers_list_output(self) -> None:
         m_quant_logger(*example_inputs)
         ref_results = extract_results_from_loggers(m_ref_logger)
         quant_results = extract_results_from_loggers(m_quant_logger)
-        comparison_results = compare_results(
-            ref_results,
-            quant_results,  # pyre-ignore[6]
-        )
+        comparison_results = compare_results(ref_results, quant_results)
         for node_summary in comparison_results.values():
             if len(node_summary.results) > 0:
                 sqnr = node_summary.results[0].sqnr
@@ -845,4 +816,4 @@ def test_extract_results_from_loggers_list_output(self) -> None:
                     for sqnr_i in sqnr:
                         self.assertGreaterEqual(sqnr_i, 35)
                 else:
-                    self.assertGreaterEqual(sqnr, 35)  # pyre-ignore[6]
+                    self.assertGreaterEqual(sqnr, 35)
diff --git a/backends/xnnpack/third-party/XNNPACK b/backends/xnnpack/third-party/XNNPACK
index 84096dd536e..52208356940 160000
--- a/backends/xnnpack/third-party/XNNPACK
+++ b/backends/xnnpack/third-party/XNNPACK
@@ -1 +1 @@
-Subproject commit 84096dd536edffd19337d9297634c4f5c5449bfd
+Subproject commit 52208356940a7c7d3597cf386d500a0f776f7bd0
diff --git a/backends/xnnpack/third-party/xnnpack.buck.bzl b/backends/xnnpack/third-party/xnnpack.buck.bzl
index 8556fde3d8a..14520b07664 100644
--- a/backends/xnnpack/third-party/xnnpack.buck.bzl
+++ b/backends/xnnpack/third-party/xnnpack.buck.bzl
@@ -4,8 +4,8 @@ load(
     "OPERATOR_SRCS",
     "SUBGRAPH_SRCS",
     "TABLE_SRCS",
-    "XNNPACK_SRCS",
     "get_xnnpack_headers",
+    "get_ukernel_config_srcs",
     "prod_srcs_for_arch_wrapper",
 )
 
@@ -274,6 +274,38 @@ def define_xnnpack():
         ],
     )
 
+    SSE2_FMA_COMPILER_FLAGS = [
+        "-msse2",
+        "-mno-sse3",
+    ]
+
+    native.cxx_library(
+        name = "ukernels_sse2fma",
+        srcs = select({
+            "DEFAULT": prod_srcs_for_arch_wrapper("sse2fma"),
+            "ovr_config//cpu:arm32": DEFAULT_DUMMY_SRC,
+            "ovr_config//cpu:arm64": DEFAULT_DUMMY_SRC,
+        }),
+        headers = get_xnnpack_headers(),
+        header_namespace = "",
+        compiler_flags = [
+            "-O2",
+            "-Wno-error=missing-braces",  # required since the SGX toolchain does not have this by default
+        ] + select({
+            "DEFAULT": SSE2_FMA_COMPILER_FLAGS,
+            "ovr_config//cpu:arm32": [],
+            "ovr_config//cpu:arm64": [],
+        }),
+        preferred_linkage = "static",
+        preprocessor_flags = [
+            "-DXNN_LOG_LEVEL=0",
+        ],
+        exported_deps = [
+            ":FP16",
+            ":interface",
+        ],
+    )
+
     SSE3_COMPILER_FLAGS = ["-mssse3"]
 
     # @lint-ignore BUCKLINT: native and fb_native are explicitly forbidden in fbcode.
@@ -961,6 +993,44 @@ def define_xnnpack():
         ],
     )
 
+    AMD64_COMPILER_FLAGS = [
+        "-mf16c",
+        "-mfma",
+        "-mavx512f",
+        "-mavx512cd",
+        "-mavx512bw",
+        "-mavx512dq",
+        "-mavx512vl",
+        "-mavx512vnni",
+        "-mgfni",
+    ]
+    native.cxx_library(
+        name = "ukernels_amd64",
+        srcs = select({
+            "DEFAULT": prod_srcs_for_arch_wrapper("amd64"),
+            "ovr_config//cpu:arm32": DEFAULT_DUMMY_SRC,
+            "ovr_config//cpu:arm64": DEFAULT_DUMMY_SRC,
+        }),
+        headers = get_xnnpack_headers(),
+        header_namespace = "",
+        compiler_flags = [
+            "-O2",
+            "-Wno-error=missing-braces",  # required since the SGX toolchain does not have this by default
+        ] + select({
+            "DEFAULT": AMD64_COMPILER_FLAGS,
+            "ovr_config//cpu:arm32": [],
+            "ovr_config//cpu:arm64": [],
+        }),
+        preferred_linkage = "static",
+        preprocessor_flags = [
+            "-DXNN_LOG_LEVEL=0",
+        ],
+        exported_deps = [
+            ":FP16",
+            ":interface",
+        ],
+    )
+
     AVX512VNNIGFNI_COMPILER_FLAGS = AVX512VNNI_COMPILER_FLAGS + [
         "-mgfni",
     ]
@@ -1044,12 +1114,14 @@ def define_xnnpack():
         ":ukernels_fma3",
         ":ukernels_sse",
         ":ukernels_sse2",
+        ":ukernels_sse2fma",
         ":ukernels_sse41",
         ":ukernels_ssse3",
         ":ukernels_avx512vbmi",
         ":ukernels_avx512vnnigfni",
         ":ukernels_avx512vnni",
         ":ukernels_avxvnni",
+        ":ukernels_amd64",
     ]
 
     ARM_XNNPACK_DEPS = [
@@ -1070,7 +1142,7 @@ def define_xnnpack():
     # @lint-ignore BUCKLINT: native and fb_native are explicitly forbidden in fbcode.
     native.cxx_library(
         name = "XNNPACK",
-        srcs = XNNPACK_SRCS + LOGGING_SRCS + [
+        srcs = get_ukernel_config_srcs() + LOGGING_SRCS + [
             "XNNPACK/src/init.c",
             "XNNPACK/src/params.c",
             "XNNPACK/src/configs/hardware-config.c",
@@ -1097,10 +1169,22 @@ def define_xnnpack():
             "-DXNN_ENABLE_GEMM_M_SPECIALIZATION",
             "-DXNN_ENABLE_ARM_DOTPROD",
             "-DXNN_ENABLE_CPUINFO",
-            # "-DXNN_ENABLE_DWCONV_MULTIPLASS=1",
+            # "-DXNN_ENABLE_DWCONV_MULTIPLASS=0",
             "-DXNN_ENABLE_ARM_I8MM=1",
             "-DXNN_ENABLE_ARM_FP16_VECTOR=1",
-            "-DXNN_ENABLE_AVX512BF16=0"
+            "-DXNN_ENABLE_AVX512F=1",
+            "-DXNN_ENABLE_AVX512SKX=1",
+            "-DXNN_ENABLE_AVX512VNNI=1",
+            "-DXNN_ENABLE_AVX512VBMI=1",
+            "-DXNN_ENABLE_AVXVNNI=0",
+            "-DXNN_ENABLE_AVXVNNIINT8=0",
+            "-DXNN_ENABLE_AVX512FP16=0",
+            "-DXNN_ENABLE_AVX512VNNIGFNI=0",
+            "-DXNN_ENABLE_AVX512BF16=0",
+            "-DXNN_ENABLE_AVX256VNNIGFNI=0",
+            "-DXNN_ENABLE_AVX512AMX=0",
+            "-DXNN_ENABLE_AVX256SKX=0",
+            "-DXNN_ENABLE_AVX256VNNI=0",
         ],
         visibility = ["PUBLIC"],
         exported_deps = COMMON_XNNPACK_DEPS + [
diff --git a/backends/xnnpack/third-party/xnnpack_src_defs.bzl b/backends/xnnpack/third-party/xnnpack_src_defs.bzl
index cb1f635e79e..25477e8c718 100644
--- a/backends/xnnpack/third-party/xnnpack_src_defs.bzl
+++ b/backends/xnnpack/third-party/xnnpack_src_defs.bzl
@@ -9,32 +9,6 @@ load("//backends/xnnpack/third-party/XNNPACK/gen:microkernels.bzl", "prod_srcs_f
 load("@fbsource//xplat/executorch/third-party:glob_defs.bzl", "subdir_glob")
 
 # To get from XNNPACK:build_srcs.bzl in the future
-_XNNPACK_SRCS = [
-    "src/configs/argmaxpool-config.c",
-    "src/configs/avgpool-config.c",
-    "src/configs/binary-elementwise-config.c",
-    "src/configs/cmul-config.c",
-    "src/configs/conv-hwc2chw-config.c",
-    "src/configs/dwconv-config.c",
-    "src/configs/dwconv2d-chw-config.c",
-    "src/configs/gemm-config.c",
-    "src/configs/ibilinear-chw-config.c",
-    "src/configs/ibilinear-config.c",
-    "src/configs/lut32norm-config.c",
-    "src/configs/maxpool-config.c",
-    "src/configs/pack-lh-config.c",
-    "src/configs/raddstoreexpminusmax-config.c",
-    "src/configs/reduce-config.c",
-    "src/configs/spmm-config.c",
-    "src/configs/transpose-config.c",
-    "src/configs/unary-elementwise-config.c",
-    "src/configs/unpool-config.c",
-    "src/configs/vmulcaddc-config.c",
-    "src/configs/x8-lut-config.c",
-    "src/configs/xx-fill-config.c",
-    "src/configs/xx-pad-config.c",
-]
-
 def define_xnnpack_build_src(xnnpack_build_src):
     return ["XNNPACK/{}".format(src) for src in xnnpack_build_src]
 
@@ -56,8 +30,12 @@ def get_xnnpack_headers():
     ])
     return src_headers | include_headers | ukernel_headers
 
+def get_ukernel_config_srcs():
+    return subdir_glob([
+        ("XNNPACK/src/configs", "*.c"),
+    ]).values()
+
 OPERATOR_SRCS = define_xnnpack_build_src(_OPERATOR_SRCS)
 SUBGRAPH_SRCS = define_xnnpack_build_src(_SUBGRAPH_SRCS)
 TABLE_SRCS = define_xnnpack_build_src(_TABLE_SRCS)
-XNNPACK_SRCS = define_xnnpack_build_src(_XNNPACK_SRCS)
 LOGGING_SRCS = define_xnnpack_build_src(_LOGGING_SRCS)
diff --git a/devtools/inspector/_inspector.py b/devtools/inspector/_inspector.py
index c797208c0c9..8694037acd8 100644
--- a/devtools/inspector/_inspector.py
+++ b/devtools/inspector/_inspector.py
@@ -1362,17 +1362,19 @@ def get_exported_program(
             else self._etrecord.graph_map.get(graph)
         )
 
-    def calculate_numeric_gap(self, distance: str = "MSE") -> pd.DataFrame:
+    def calculate_numeric_gap(self, distance: str = "MSE"):
         """
         Compares logged intermediate outputs from the exported graph (in ETRecord)
         with runtime outputs (in ETDump) using a user-specific numerical comparator.
+        To use this function, you must first generate the ETRecord using the `bundle_program`,
+        and then create the Inspector instance with the ETRecord and ETDump. The Inspector can then
+        compare the intermediate outputs from the AOT and the runtime.
 
         Args:
             distance: the metrics the inspector will use for gap calculation. Should be one of "MSE", "L1" and "SNR".
 
         Returns:
-            pd.DataFrame: A DataFrame listing corresponding operator outputs from
-                          both stages and their computed numerical gaps.
+            pd.DataFrame: A DataFrame listing corresponding operator intermediate outputs from both stages and their computed numerical gaps.
         """
         aot_intermediate_outputs, aot_debug_handle_to_op_name = (
             self._get_aot_intermediate_outputs_and_op_names()
diff --git a/docs/README.md b/docs/README.md
index e30decb9362..eed16df1b8d 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -40,7 +40,7 @@ To build the documentation locally:
 1. Clone the ExecuTorch repo to your machine.
 
    ```bash
-   git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+   git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
    ```
 
 1. If you don't have it already, start either a Python virtual envitonment:
diff --git a/docs/source/_static/img/calculate_numeric_gap.png b/docs/source/_static/img/calculate_numeric_gap.png
new file mode 100644
index 00000000000..d63f016d042
Binary files /dev/null and b/docs/source/_static/img/calculate_numeric_gap.png differ
diff --git a/docs/source/_static/img/swiftpm_xcode1.png b/docs/source/_static/img/swiftpm_xcode1.png
index 3fcad383610..4e624ed43df 100644
Binary files a/docs/source/_static/img/swiftpm_xcode1.png and b/docs/source/_static/img/swiftpm_xcode1.png differ
diff --git a/docs/source/backend-template.md b/docs/source/backend-template.md
index 5dc5f739671..bf992c1ffab 100644
--- a/docs/source/backend-template.md
+++ b/docs/source/backend-template.md
@@ -32,6 +32,8 @@ What quantization schemes does this backend support? Consider including the foll
 - Symmetric vs asymmetric weights?
 - Per-tensor, per-chanel, group/blockwise?
 
+If using a PT2E quantizer, document how to initialize the quantizer and all relevant configs and options.
+
 Include a code snippet demonstrating how to perform quantization for this backend. Document, or link to, a description of the parameters that the user can specify.
 
 ## Runtime Integration
diff --git a/docs/source/backends-arm-ethos-u.md b/docs/source/backends-arm-ethos-u.md
index d4531668c36..4dc616f3461 100644
--- a/docs/source/backends-arm-ethos-u.md
+++ b/docs/source/backends-arm-ethos-u.md
@@ -35,15 +35,12 @@ from executorch.exir import (
 )
 from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
 from torchvision.models import mobilenetv2
+import executorch.kernels.quantized
 
 mobilenet_v2 = mobilenetv2.mobilenet_v2(
     weights=mobilenetv2.MobileNet_V2_Weights.DEFAULT
 ).eval()
 example_inputs = (torch.randn(1, 3, 224, 224),)
-# .so suffix is .dylib on MacOS.
-torch.ops.load_library(
-    "cmake-out-aot-lib/kernels/quantized/libquantized_ops_aot_lib.so"
-)
 
 compile_spec = ArmCompileSpecBuilder().ethosu_compile_spec(
         "ethos-u55-128",
diff --git a/docs/source/backends-coreml.md b/docs/source/backends-coreml.md
index 37c89b56a54..dbf87e7d697 100644
--- a/docs/source/backends-coreml.md
+++ b/docs/source/backends-coreml.md
@@ -18,7 +18,7 @@ Below are the minimum OS requirements on various hardware for running a CoreML-d
 ## Development Requirements
 To develop you need:
 
-- [macOS](https://developer.apple.com/macos) >= 13.0.
+- [macOS](https://developer.apple.com/macos) >= 13.0
 - [Xcode](https://developer.apple.com/documentation/xcode) >= 14.1
 
 
@@ -55,43 +55,44 @@ with open("mv2_coreml.pte", "wb") as file:
 
 ### Partitioner API
 
-The CoreML partitioner API allows for configuration of the model delegation to CoreML. Passing an `CoreMLPartitioner` instance with no additional parameters will run as much of the model as possible on the CoreML backend with default settings. This is the most common use-case. For advanced use cases, the partitioner exposes the following options via the [constructor](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/partition/coreml_partitioner.py#L60):
+The CoreML partitioner API allows for configuration of the model delegation to CoreML. Passing a `CoreMLPartitioner` instance with no additional parameters will run as much of the model as possible on the CoreML backend with default settings. This is the most common use case. For advanced use cases, the partitioner exposes the following options via the [constructor](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/partition/coreml_partitioner.py#L60):
 
 
  - `skip_ops_for_coreml_delegation`: Allows you to skip ops for delegation by CoreML.  By default, all ops that CoreML supports will be delegated.  See [here](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/test/test_coreml_partitioner.py#L42) for an example of skipping an op for delegation.
-- `compile_specs`: A list of CompileSpec for the CoreML backend.  These control low-level details of CoreML delegation, such as the compute unit (CPU, GPU, ANE), the iOS deployment target, and the compute precision (FP16, FP32).  These are discussed more below.
-- `take_over_mutable_buffer`: A boolean that indicates whether PyTorch mutable buffers in stateful models should be converted to [CoreML MLState](https://developer.apple.com/documentation/coreml/mlstate).  If set to false, mutable buffers in the PyTorch graph are converted to graph inputs and outputs to the CoreML lowered module under the hood.  Generally setting take_over_mutable_buffer to true will result in better performance, but using MLState requires iOS >= 18.0, macOS >= 15.0, and XCode >= 16.0.
+- `compile_specs`: A list of `CompileSpec`s for the CoreML backend.  These control low-level details of CoreML delegation, such as the compute unit (CPU, GPU, ANE), the iOS deployment target, and the compute precision (FP16, FP32).  These are discussed more below.
+- `take_over_mutable_buffer`: A boolean that indicates whether PyTorch mutable buffers in stateful models should be converted to [CoreML `MLState`](https://developer.apple.com/documentation/coreml/mlstate).  If set to `False`, mutable buffers in the PyTorch graph are converted to graph inputs and outputs to the CoreML lowered module under the hood.  Generally, setting `take_over_mutable_buffer` to true will result in better performance, but using `MLState` requires iOS >= 18.0, macOS >= 15.0, and Xcode >= 16.0.
 
 #### CoreML CompileSpec
 
-A list of CompileSpec is constructed with [CoreMLBackend.generate_compile_specs](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L210).  Below are the available options:
-- `compute_unit`: this controls the compute units (CPU, GPU, ANE) that are used by CoreML.  The default value is coremltools.ComputeUnit.ALL.  The available options from coremltools are:
-    - coremltools.ComputeUnit.ALL (uses the CPU, GPU, and ANE)
-    - coremltools.ComputeUnit.CPU_ONLY (uses the CPU only)
-    - coremltools.ComputeUnit.CPU_AND_GPU (uses both the CPU and GPU, but not the ANE)
-    - coremltools.ComputeUnit.CPU_AND_NE (uses both the CPU and ANE, but not the GPU)
-- `minimum_deployment_target`: The minimum iOS deployment target (e.g., coremltools.target.iOS18).  The default value is coremltools.target.iOS15.
-- `compute_precision`: The compute precision used by CoreML (coremltools.precision.FLOAT16, coremltools.precision.FLOAT32).  The default value is coremltools.precision.FLOAT16.  Note that the compute precision is applied no matter what dtype is specified in the exported PyTorch model.  For example, an FP32 PyTorch model will be converted to FP16 when delegating to the CoreML backend by default.  Also note that the ANE only supports FP16 precision.
-- `model_type`: Whether the model should be compiled to the CoreML [mlmodelc format](https://developer.apple.com/documentation/coreml/downloading-and-compiling-a-model-on-the-user-s-device) during .pte creation ([CoreMLBackend.MODEL_TYPE.COMPILED_MODEL](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L71)), or whether it should be compiled to mlmodelc on device ([CoreMLBackend.MODEL_TYPE.MODEL](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L70)).  Using CoreMLBackend.MODEL_TYPE.COMPILED_MODEL and doing compilation ahead of time should improve the first time on-device model load time.
+A list of `CompileSpec`s is constructed with [`CoreMLBackend.generate_compile_specs`](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L210).  Below are the available options:
+- `compute_unit`: this controls the compute units (CPU, GPU, ANE) that are used by CoreML.  The default value is `coremltools.ComputeUnit.ALL`.  The available options from coremltools are:
+    - `coremltools.ComputeUnit.ALL` (uses the CPU, GPU, and ANE)
+    - `coremltools.ComputeUnit.CPU_ONLY` (uses the CPU only)
+    - `coremltools.ComputeUnit.CPU_AND_GPU` (uses both the CPU and GPU, but not the ANE)
+    - `coremltools.ComputeUnit.CPU_AND_NE` (uses both the CPU and ANE, but not the GPU)
+- `minimum_deployment_target`: The minimum iOS deployment target (e.g., `coremltools.target.iOS18`).  The default value is `coremltools.target.iOS15`.
+- `compute_precision`: The compute precision used by CoreML (`coremltools.precision.FLOAT16` or `coremltools.precision.FLOAT32`).  The default value is `coremltools.precision.FLOAT16`.  Note that the compute precision is applied no matter what dtype is specified in the exported PyTorch model.  For example, an FP32 PyTorch model will be converted to FP16 when delegating to the CoreML backend by default.  Also note that the ANE only supports FP16 precision.
+- `model_type`: Whether the model should be compiled to the CoreML [mlmodelc format](https://developer.apple.com/documentation/coreml/downloading-and-compiling-a-model-on-the-user-s-device) during .pte creation ([`CoreMLBackend.MODEL_TYPE.COMPILED_MODEL`](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L71)), or whether it should be compiled to mlmodelc on device ([`CoreMLBackend.MODEL_TYPE.MODEL`](https://github.com/pytorch/executorch/blob/14ff52ff89a89c074fc6c14d3f01683677783dcd/backends/apple/coreml/compiler/coreml_preprocess.py#L70)).  Using `CoreMLBackend.MODEL_TYPE.COMPILED_MODEL` and doing compilation ahead of time should improve the first time on-device model load time.
 
 ### Testing the Model
 
-After generating the CoreML-delegated .pte, the model can be tested from Python using the ExecuTorch runtime python bindings. This can be used to sanity check the model and evaluate numerical accuracy. See [Testing the Model](using-executorch-export.md#testing-the-model) for more information.
+After generating the CoreML-delegated .pte, the model can be tested from Python using the ExecuTorch runtime Python bindings. This can be used to quickly check the model and evaluate numerical accuracy. See [Testing the Model](using-executorch-export.md#testing-the-model) for more information.
 
 ----
 
 ### Quantization
 
-To quantize a PyTorch model for the CoreML backend, use the `CoreMLQuantizer`. `Quantizers` are backend specific, and the `CoreMLQuantizer` is configured to quantize models to leverage the available quantization for the CoreML backend.
+To quantize a PyTorch model for the CoreML backend, use the `CoreMLQuantizer`.
 
 ### 8-bit Quantization using the PT2E Flow
 
-To perform 8-bit quantization with the PT2E flow, perform the following steps:
+Quantization with the CoreML backend requires exporting the model for iOS 17 or later.
+To perform 8-bit quantization with the PT2E flow, follow these steps:
 
-1) Define [coremltools.optimize.torch.quantization.LinearQuantizerConfig](https://apple.github.io/coremltools/source/coremltools.optimize.torch.quantization.html#coremltools.optimize.torch.quantization.LinearQuantizerConfig) and use to to create an instance of a `CoreMLQuantizer`.
+1) Create a [`coremltools.optimize.torch.quantization.LinearQuantizerConfig`](https://apple.github.io/coremltools/source/coremltools.optimize.torch.quantization.html#coremltools.optimize.torch.quantization.LinearQuantizerConfig) and use to to create an instance of a `CoreMLQuantizer`.
 2) Use `torch.export.export_for_training` to export a graph module that will be prepared for quantization.
 3) Call `prepare_pt2e` to prepare the model for quantization.
-4) For static quantization, run the prepared model with representative samples to calibrate the quantizated tensor activation ranges.
+4) Run the prepared model with representative samples to calibrate the quantizated tensor activation ranges.
 5) Call `convert_pt2e` to quantize the model.
 6) Export and lower the model using the standard flow.
 
@@ -112,18 +113,17 @@ mobilenet_v2 = models.mobilenetv2.mobilenet_v2(weights=MobileNet_V2_Weights.DEFA
 sample_inputs = (torch.randn(1, 3, 224, 224), )
 
 # Step 1: Define a LinearQuantizerConfig and create an instance of a CoreMLQuantizer
-quantization_config = ct.optimize.torch.quantization.LinearQuantizerConfig.from_dict(
-    {
-        "global_config": {
-            "quantization_scheme": ct.optimize.torch.quantization.QuantizationScheme.symmetric,
-            "milestones": [0, 0, 10, 10],
-            "activation_dtype": torch.quint8,
-            "weight_dtype": torch.qint8,
-            "weight_per_channel": True,
-        }
-    }
+# Note that "linear" here does not mean only linear layers are quantized, but that linear (aka affine) quantization
+# is being performed
+static_8bit_config = ct.optimize.torch.quantization.LinearQuantizerConfig(
+    global_config=ct.optimize.torch.quantization.ModuleLinearQuantizerConfig(
+        quantization_scheme="symmetric",
+        activation_dtype=torch.quint8,
+        weight_dtype=torch.qint8,
+        weight_per_channel=True,
+    )
 )
-quantizer = CoreMLQuantizer(quantization_config)
+quantizer = CoreMLQuantizer(static_8bit_config)
 
 # Step 2: Export the model for training
 training_gm = torch.export.export_for_training(mobilenet_v2, sample_inputs).module()
@@ -153,17 +153,36 @@ et_program = to_edge_transform_and_lower(
 ).to_executorch()
 ```
 
-See [PyTorch 2 Export Post Training Quantization](https://pytorch.org/tutorials/prototype/pt2e_quant_ptq.html) for more information.
+The above does static quantization (activations and weights are quantized).
+
+You can see a full description of available quantization configs in the [coremltools documentation](https://apple.github.io/coremltools/source/coremltools.optimize.torch.quantization.html#coremltools.optimize.torch.quantization.LinearQuantizerConfig).  For example, the config below will perform weight-only quantization:
+
+```
+weight_only_8bit_config = ct.optimize.torch.quantization.LinearQuantizerConfig(
+    global_config=ct.optimize.torch.quantization.ModuleLinearQuantizerConfig(
+        quantization_scheme="symmetric",
+        activation_dtype=torch.float32,
+        weight_dtype=torch.qint8,
+        weight_per_channel=True,
+    )
+)
+quantizer = CoreMLQuantizer(weight_only_8bit_config)
+```
+
+Quantizing activations requires calibrating the model on representative data.  Also note that PT2E currently requires passing at least 1 calibration sample before calling `convert_pt2e`, even for data-free weight-only quantization.
+
+See [PyTorch 2 Export Post Training Quantization](https://docs.pytorch.org/ao/main/tutorials_source/pt2e_quant_ptq.html) for more information.
+
 
 ----
 
 ## Runtime integration
 
-To run the model on-device, use the standard ExecuTorch runtime APIs. See [Running on Device](getting-started.md#running-on-device) for more information, including building the iOS frameworks.
+To run the model on device, use the standard ExecuTorch runtime APIs. See [Running on Device](getting-started.md#running-on-device) for more information, including building the iOS frameworks.
 
 When building from source, pass `-DEXECUTORCH_BUILD_COREML=ON` when configuring the CMake build to compile the CoreML backend.
 
-To link against the `coremldelegate` target. Due to the use of static registration, it may be necessary to link with whole-archive. This can typically be done by passing `"$<LINK_LIBRARY:WHOLE_ARCHIVE,coremldelegate>"` to `target_link_libraries`.
+Due to the use of static initializers for registration, it may be necessary to use whole-archive to link against the `coremldelegate` target. This can typically be done by passing `"$<LINK_LIBRARY:WHOLE_ARCHIVE,coremldelegate>"` to `target_link_libraries`.
 
 ```
 # CMakeLists.txt
@@ -175,7 +194,7 @@ target_link_libraries(
     extension_module_static
     extension_tensor
     optimized_native_cpu_ops_lib
-    coremldelegate)
+    $<LINK_LIBRARY:WHOLE_ARHIVE,coremldelegate>)
 ```
 
 No additional steps are necessary to use the backend beyond linking the target. A CoreML-delegated .pte file will automatically run on the registered backend.
@@ -198,9 +217,14 @@ Note that if the ExecuTorch model has graph breaks, there may be multiple extrac
 ### During lowering
 1. "ValueError: In op, of type [X], named [Y], the named input [Z] must have the same data type as the named input x. However, [Z] has dtype fp32 whereas x has dtype fp16."
 
-This happens because the model is in FP16, but CoreML interprets some of the arguments as FP32, which leads to a type mismatch.  The solution is to keep the PyTorch model in FP32.  Note that the model will be still be converted to FP16 during lowering to CoreML unless specified otherwise in the compute_precision [CoreML CompileSpec](#coreml-compilespec).  Also see the [related issue in coremltools](https://github.com/apple/coremltools/issues/2480).
+This happens because the model is in FP16, but CoreML interprets some of the arguments as FP32, which leads to a type mismatch.  The solution is to keep the PyTorch model in FP32.  Note that the model will be still be converted to FP16 during lowering to CoreML unless specified otherwise in the compute_precision [CoreML `CompileSpec`](#coreml-compilespec).  Also see the [related issue in coremltools](https://github.com/apple/coremltools/issues/2480).
 
 2. coremltools/converters/mil/backend/mil/load.py", line 499, in export
     raise RuntimeError("BlobWriter not loaded")
 
-If you're using Python 3.13, try reducing your python version to Python 3.12.  coremltools does not support Python 3.13, see this [issue](https://github.com/apple/coremltools/issues/2487).  
+If you're using Python 3.13, try reducing your python version to Python 3.12.  coremltools does not support Python 3.13 per [coremltools issue #2487](https://github.com/apple/coremltools/issues/2487).
+
+### At runtime
+1. [ETCoreMLModelCompiler.mm:55] [Core ML]  Failed to compile model, error = Error Domain=com.apple.mlassetio Code=1 "Failed to parse the model specification. Error: Unable to parse ML Program: at unknown location: Unknown opset 'CoreML7'." UserInfo={NSLocalizedDescription=Failed to par$
+
+This means the model requires the the CoreML opset 'CoreML7', which requires running the model on iOS >= 17 or macOS >= 14.
diff --git a/docs/source/backends-mps.md b/docs/source/backends-mps.md
index 0d86c8e5c64..0dcf8b13c13 100644
--- a/docs/source/backends-mps.md
+++ b/docs/source/backends-mps.md
@@ -40,7 +40,9 @@ In order to be able to successfully build and run a model using the MPS backend
 
 ## Setting up Developer Environment
 
-***Step 1.*** Please finish tutorial [Getting Started](getting-started.md).
+***Step 1.*** Complete the steps in [Getting Started](getting-started.md) to set up the ExecuTorch development environment.
+
+You will also need a local clone of the ExecuTorch repository. See [Building ExecuTorch from Source](using-executorch-building-from-source.html) for instructions. All commands in this document should be run from the executorch repository.
 
 ## Build
 
@@ -70,12 +72,12 @@ cd executorch
 ## Run the mv3 generated model using the mps_executor_runner
 
 ```bash
-./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_bundled_fp16.pte --bundled_program
+./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_float16_bundled.pte --bundled_program
 ```
 
 - You should see the following results. Note that no output file will be generated in this example:
 ```
-I 00:00:00.003290 executorch:mps_executor_runner.mm:286] Model file mv3_mps_bundled_fp16.pte is loaded.
+I 00:00:00.003290 executorch:mps_executor_runner.mm:286] Model file mv3_mps_float16_bundled.pte is loaded.
 I 00:00:00.003306 executorch:mps_executor_runner.mm:292] Program methods: 1
 I 00:00:00.003308 executorch:mps_executor_runner.mm:294] Running method forward
 I 00:00:00.003311 executorch:mps_executor_runner.mm:349] Setting up non-const buffer 1, size 606112.
@@ -112,11 +114,11 @@ python3 -m examples.apple.mps.scripts.mps_example --model_name="mv3" --generate_
 ```
 2. Run your Program on the ExecuTorch runtime and generate an [ETDump](etdump.md).
 ```
-./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_bundled_fp16.pte --bundled_program --dump-outputs
+./cmake-out/examples/apple/mps/mps_executor_runner --model_path mv3_mps_float16_bundled.pte --bundled_program --dump-outputs
 ```
 3. Create an instance of the Inspector API by passing in the ETDump you have sourced from the runtime along with the optionally generated ETRecord from step 1.
 ```bash
-python3 -m sdk.inspector.inspector_cli --etdump_path etdump.etdp --etrecord_path etrecord.bin
+python3 -m devtools.inspector.inspector_cli --etdump_path etdump.etdp --etrecord_path etrecord.bin
 ```
 
 ## Deploying and Running on Device
diff --git a/docs/source/backends-nxp.md b/docs/source/backends-nxp.md
new file mode 100644
index 00000000000..f02f495f685
--- /dev/null
+++ b/docs/source/backends-nxp.md
@@ -0,0 +1,5 @@
+# NXP eIQ Neutron Backend
+
+See
+[NXP eIQ Neutron Backend](https://github.com/pytorch/executorch/blob/main/backends/nxp/README.md)
+for current status about running ExecuTorch on NXP eIQ Neutron Backend.
diff --git a/docs/source/backends-qualcomm.md b/docs/source/backends-qualcomm.md
index 73bdefe45d7..54d0d3f8e1e 100644
--- a/docs/source/backends-qualcomm.md
+++ b/docs/source/backends-qualcomm.md
@@ -116,7 +116,7 @@ export PYTHONPATH=$EXECUTORCH_ROOT/..
 
 An example script for the below building instructions is [here](https://github.com/pytorch/executorch/blob/main/backends/qualcomm/scripts/build.sh).
 We recommend to use the script because the ExecuTorch build-command can change from time to time.
-The above script is actively used. It is updated more frquently than this tutorial.
+The above script is actively used. It is updated more frequently than this tutorial.
 An example usage is
 ```bash
 cd $EXECUTORCH_ROOT
@@ -154,14 +154,14 @@ cmake --build $PWD --target "PyQnnManagerAdaptor" "PyQnnWrapperAdaptor" -j$(npro
 cp -f backends/qualcomm/PyQnnManagerAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python
 cp -f backends/qualcomm/PyQnnWrapperAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python
 
-# Workaround for fbs files in exir/_serialize
+# Workaround for .fbs files in exir/_serialize
 cp $EXECUTORCH_ROOT/schema/program.fbs $EXECUTORCH_ROOT/exir/_serialize/program.fbs
 cp $EXECUTORCH_ROOT/schema/scalar_type.fbs $EXECUTORCH_ROOT/exir/_serialize/scalar_type.fbs
 ```
 
 ### Runtime:
 
-A example `qnn_executor_runner` executable would be used to run the compiled `pte` model.
+An example `qnn_executor_runner` executable would be used to run the compiled `pte` model.
 
 Commands to build `qnn_executor_runner` for Android:
 
@@ -199,7 +199,7 @@ cmake ../examples/qualcomm \
 cmake --build examples/qualcomm -j$(nproc)
 
 # qnn_executor_runner can be found under examples/qualcomm
-# The full path is $EXECUTORCH_ROOT/build-android/examples/qualcomm/qnn_executor_runner
+# The full path is $EXECUTORCH_ROOT/build-android/examples/qualcomm/executor_runner/qnn_executor_runner
 ls examples/qualcomm
 ```
 
@@ -255,12 +255,12 @@ cmake ../examples/qualcomm \
 
 cmake --build examples/qualcomm -j$(nproc)
 
-# qnn_executor_runner can be found under examples/qualcomm
-# The full path is $EXECUTORCH_ROOT/build-x86/examples/qualcomm/qnn_executor_runner
-ls examples/qualcomm/
+# qnn_executor_runner can be found under examples/qualcomm/executor_runner
+# The full path is $EXECUTORCH_ROOT/build-x86/examples/qualcomm/executor_runner/qnn_executor_runner
+ls examples/qualcomm/executor_runner
 ```
 
-To run the HTP emulator, the dynamic linker need to access QNN libraries and `libqnn_executorch_backend.so`.
+To run the HTP emulator, the dynamic linker needs to access QNN libraries and `libqnn_executorch_backend.so`.
 We set the below two paths to `LD_LIBRARY_PATH` environment variable:
   1. `$QNN_SDK_ROOT/lib/x86_64-linux-clang/`
   2. `$EXECUTORCH_ROOT/build-x86/lib/`
@@ -273,7 +273,7 @@ So, we can run `./deeplab_v3/dlv3_qnn.pte` by:
 ```bash
 cd $EXECUTORCH_ROOT/build-x86
 export LD_LIBRARY_PATH=$EXECUTORCH_ROOT/build-x86/lib/:$LD_LIBRARY_PATH
-examples/qualcomm/qnn_executor_runner --model_path ../deeplab_v3/dlv3_qnn.pte
+examples/qualcomm/executor_runner/qnn_executor_runner --model_path ../deeplab_v3/dlv3_qnn.pte
 ```
 
 We should see some outputs like the below. Note that the emulator can take some time to finish.
diff --git a/docs/source/backends-xnnpack.md b/docs/source/backends-xnnpack.md
index b6bd1eab7c6..b7fca261850 100644
--- a/docs/source/backends-xnnpack.md
+++ b/docs/source/backends-xnnpack.md
@@ -117,7 +117,43 @@ et_program = to_edge_transform_and_lower( # (6)
 ).to_executorch()
 ```
 
-See [PyTorch 2 Export Post Training Quantization](https://pytorch.org/tutorials/prototype/pt2e_quant_ptq.html) for more information.
+See [PyTorch 2 Export Post Training Quantization](https://docs.pytorch.org/ao/main/tutorials_source/pt2e_quant_ptq.html) for more information.
+
+### LLM quantization with quantize_
+
+The XNNPACK backend also supports quantizing models with the [torchao](https://github.com/pytorch/ao) quantize_ API.  This is most commonly used for LLMs, requiring more advanced quantization.  Since quantize_ is not backend aware, it is important to use a config that is compatible with CPU/XNNPACK:
+
+* Quantize embeedings with IntxWeightOnlyConfig (with weight_dtype torch.int2, torch.int4, or torch.int8, using PerGroup or PerAxis granularity)
+* Quantize linear layers with Int8DynamicActivationIntxWeightConfig (with weight_dtype=torch.int4, using PerGroup or PerAxis granularity)
+
+Below is a simple example, but a more detailed tutorial including accuracy evaluation on popular LLM benchmarks can be found in the [torchao documentation](https://docs.pytorch.org/ao/main/serving.html#mobile-deployment-with-executorch).
+
+```python
+from torchao.quantization.granularity import PerGroup, PerAxis
+from torchao.quantization.quant_api import (
+    IntxWeightOnlyConfig,
+    Int8DynamicActivationIntxWeightConfig,
+    quantize_,
+)
+
+# Quantize embeddings with 8-bits, per channel
+embedding_config = IntxWeightOnlyConfig(
+    weight_dtype=torch.int8,
+    granularity=PerAxis(0),
+)
+qunatize_(
+    eager_model,
+    lambda m, fqn: isinstance(m, torch.nn.Embedding),
+)
+
+
+# Quatize linear layers with 8-bit dynamic activations and 4-bit weights
+linear_config = Int8DynamicActivationIntxWeightConfig(
+    weight_dtype=torch.int4,
+    weight_granularity=PerGroup(32),
+)
+quantize_(eager_model, linear_config)
+```
 
 ----
 
diff --git a/docs/source/devtools-overview.md b/docs/source/devtools-overview.md
index 6d2f0e6375a..449dd1485dc 100644
--- a/docs/source/devtools-overview.md
+++ b/docs/source/devtools-overview.md
@@ -15,6 +15,7 @@ The ExecuTorch Developer Tools support the following features:
 - **Delegate Integration** - Surfacing performance details from delegate backends
     - Link back delegate operator execution to the nodes they represent in the edge dialect graph (and subsequently linking back to source code and module hierarchy)
 - **Debugging** - Intermediate outputs and output quality analysis
+- **Numerical Discrepancy Detection** - Operator-level numerical discrepancy detection between AOT and runtime intermediate outputs to streamline numerical debugging and validation.
 - **Memory Allocation Insights** - Visualize how memory is planned, where all the live tensors are at any point in time
 - **Visualization** - Coming soon
 
diff --git a/docs/source/extension-module.md b/docs/source/extension-module.md
index 835c5c12e27..24f16aa8a3a 100644
--- a/docs/source/extension-module.md
+++ b/docs/source/extension-module.md
@@ -43,6 +43,11 @@ Creating a `Module` object is a fast operation that does not involve significant
 Module module("/path/to/model.pte");
 ```
 
+For a model with data separated into a PTD file, load them together:
+```cpp
+Module module("/path/to/model.pte", "/path/to/model.ptd");
+```
+
 ### Force-Loading a Method
 
 To force-load the `Module` (and thus the underlying ExecuTorch `Program`) at any time, use the `load()` function:
diff --git a/docs/source/getting-started.md b/docs/source/getting-started.md
index be15e7d6ea2..ce9283dfa2b 100644
--- a/docs/source/getting-started.md
+++ b/docs/source/getting-started.md
@@ -168,7 +168,7 @@ ExecuTorch provides C++ APIs, which can be used to target embedded or mobile dev
 CMake is the preferred build system for the ExecuTorch C++ runtime. To use with CMake, clone the ExecuTorch repository as a subdirectory of your project, and use CMake's `add_subdirectory("executorch")` to include the dependency. The `executorch` target, as well as kernel and backend targets will be made available to link against. The runtime can also be built standalone to support diverse toolchains. See [Using ExecuTorch with C++](using-executorch-cpp.md) for a detailed description of build integration, targets, and cross compilation.
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git
+git clone -b release/0.7 https://github.com/pytorch/executorch.git
 ```
 ```python
 # CMakeLists.txt
diff --git a/docs/source/index.md b/docs/source/index.md
index 49a51d4e557..f0ec1d2c6b3 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -21,8 +21,7 @@ ExecuTorch provides support for:
 * **Strong Model Support** LLMs (Large Language Models),
   CV (Computer Vision), ASR (Automatic Speech Recognition), TTS (Text To Speech)
 * **All Major Platforms** Android, Mac, Linux, Windows
-* **Rich Acceleration Support** Apple, Arm, Cadence, MediaTek,
-  Qualcomm, Vulkan, XNNPACK
+* **Rich Acceleration Support** Apple, Arm, Cadence, MediaTek, NXP, OpenVino, Qualcomm, Vulkan, XNNPACK
 
 ### Documentation Navigation
 #### Introduction
@@ -39,6 +38,7 @@ ExecuTorch provides support for:
 - [Runtime Integration](using-executorch-runtime-integration)
 - [Troubleshooting](using-executorch-troubleshooting)
 - [Building from Source](using-executorch-building-from-source)
+- [Quantization](quantization-overview)
 - [FAQs](using-executorch-faqs)
 #### Examples
 - [Android Demo Apps](https://github.com/pytorch-labs/executorch-examples/tree/main/dl3/android/DeepLabV3Demo#executorch-android-demo-app)
@@ -54,6 +54,8 @@ ExecuTorch provides support for:
 - [Qualcomm](backends-qualcomm)
 - [MediaTek](backends-mediatek)
 - [Cadence](backends-cadence)
+- [OpenVINO](build-run-openvino)
+- [NXP](backend-nxp)
 #### Developer Tools
 - [Overview](devtools-overview)
 - [Bundled IO](bundled-io)
@@ -74,24 +76,25 @@ ExecuTorch provides support for:
 - [Platform Abstraction Layer](runtime-platform-abstraction-layer)
 #### Portable C++ Programming
 - [PTE File Format](pte-file-format)
+- [PTD File Format](ptd-file-format)
 #### API Reference
 - [Export to Executorch API Reference](export-to-executorch-api-reference)
 - [Executorch Runtime API Reference](executorch-runtime-api-reference)
 - [Runtime Python API Reference](runtime-python-api-reference)
 - [API Life Cycle](api-life-cycle)
 - [Javadoc](https://pytorch.org/executorch/main/javadoc/)
-#### Quantization
-- [Overview](quantization-overview)
 #### Kernel Library
 - [Overview](kernel-library-overview)
 - [Custom ATen Kernel](kernel-library-custom-aten-kernel)
 - [Selective Build](kernel-library-selective-build)
 #### Working with LLMs
-- [Llama](llm/llama.md)
-- [Llama on Android](llm/llama-demo-android.md)
-- [Llama on iOS](llm/llama-demo-ios.md)
-- [Llama on Android via Qualcomm backend](llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md)
-- [Intro to LLMs in Executorch](llm/getting-started.md)
+- [Getting Started](llm/getting-started.md)
+- [Exporting LLMs](llm/export-llm.md)
+- [Exporting custom LLMs](llm/export-custom-llm.md)
+- [Running with C++](llm/run-with-c-plus-plus.md)
+- [Running on Android (XNNPack)](llm/llama-demo-android.md)
+- [Running on Android (QNN)](llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md)
+- [Running on iOS](llm/run-on-ios.md)
 #### Backend Development
 - [Delegates Integration](backend-delegates-integration)
 - [XNNPACK Reference](backend-delegates-xnnpack-reference)
@@ -163,6 +166,8 @@ backends-arm-ethos-u
 backends-qualcomm
 backends-mediatek
 backends-cadence
+OpenVINO Backend <build-run-openvino>
+backends-nxp
 ```
 
 ```{toctree}
@@ -197,6 +202,7 @@ runtime-backend-delegate-implementation-and-linking
 runtime-platform-abstraction-layer
 portable-cpp-programming
 pte-file-format
+ptd-file-format
 ```
 
 ```{toctree}
@@ -238,11 +244,13 @@ kernel-library-selective-build
 :caption: Working with LLMs
 :hidden:
 
-Llama <llm/llama>
-Llama on Android <llm/llama-demo-android>
-Llama on iOS <llm/llama-demo-ios>
-Llama on Android via Qualcomm backend <llm/build-run-llama3-qualcomm-ai-engine-direct-backend>
-Intro to LLMs in Executorch <llm/getting-started>
+Getting Started <llm/getting-started>
+Exporting LLMs with export_llm <llm/export-llm>
+Exporting custom LLMs <llm/export-custom-llm>
+Running with C++ <llm/run-with-c-plus-plus>
+Running on Android <XNNPack> <llm/llama-demo-android>
+Running on Android <QNN> <llm/build-run-llama3-qualcomm-ai-engine-direct-backend>
+Running on iOS <llm/run-on-ios>
 ```
 
 ```{toctree}
diff --git a/docs/source/kernel-library-selective-build.md b/docs/source/kernel-library-selective-build.md
index f9a991767a3..3e9400f4d6a 100644
--- a/docs/source/kernel-library-selective-build.md
+++ b/docs/source/kernel-library-selective-build.md
@@ -36,17 +36,20 @@ The basic flow looks like this:
 
 ## APIs
 
-We expose a CMake macro `[gen_selected_ops](https://github.com/pytorch/executorch/blob/main/tools/cmake/Codegen.cmake#L12)`, to allow users specifying op info:
+We expose a CMake macro [gen_selected_ops](https://github.com/pytorch/executorch/blob/main/tools/cmake/Codegen.cmake#L12), to allow users specifying op info:
 
 ```
 gen_selected_ops(
-  LIB_NAME         # the name of the selective build operator library to be generated
-  OPS_SCHEMA_YAML  # path to a yaml file containing operators to be selected
-  ROOT_OPS         # comma separated operator names to be selected
-  INCLUDE_ALL_OPS  # boolean flag to include all operators
+  LIB_NAME              # the name of the selective build operator library to be generated
+  OPS_SCHEMA_YAML       # path to a yaml file containing operators to be selected
+  ROOT_OPS              # comma separated operator names to be selected
+  INCLUDE_ALL_OPS       # boolean flag to include all operators
+  OPS_FROM_MODEL        # path to a pte file of model to select operators from
+  DTYPE_SELECTIVE_BUILD # boolean flag to enable dtye selection
 )
 ```
 
+The macro makes a call to gen_oplist.py, which requires a [distinct selection](https://github.com/BujSet/executorch/blob/main/codegen/tools/gen_oplist.py#L222-L228) of API choice. `OPS_SCHEMA_YAML`, `ROOT_OPS`, `INCLUDE_ALL_OPS`, and `OPS_FROM_MODEL` are mutually exclusive options, and should not be used in conjunction. 
 
 ### Select all ops
 
@@ -62,31 +65,29 @@ Context: each kernel library is designed to have a yaml file associated with it.
 
 This API lets users pass in a list of operator names. Note that this API can be combined with the API above and we will create a allowlist from the union of both API inputs.
 
+### Select ops from model
 
-## Example Walkthrough
+This API lets users pass in a pte file of an exported model. When used, the pte file will be parsed to generate a yaml file that enumerates the operators and dtypes used in the model. 
 
-In CMakeLists.txt we have the following logic:
-```cmake
-set(_kernel_lib)
-if(SELECT_ALL_OPS)
-  gen_selected_ops("" "" "${SELECT_ALL_OPS}")
-elseif(SELECT_OPS_LIST)
-  gen_selected_ops("" "${SELECT_OPS_LIST}" "")
-elseif(SELECT_OPS_YAML)
- set(_custom_ops_yaml ${EXECUTORCH_ROOT}/examples/portable/custom_ops/custom_ops.yaml)
-  gen_selected_ops("${_custom_ops_yaml}" "" "")
-endif()
-```
-Then when calling CMake, we can do:
+### Dtype Selective Build
 
-```
-cmake -D… -DSELECT_OPS_LIST="aten::add.out,aten::mm.out”
-```
+Beyond pruning the binary to remove unused operators, the binary size can further reduced by removing unused dtypes. For example, if your model only uses floats for the `add` operator, then including variants of the `add` operators for `doubles` and `ints` is unnecessary. The flag `DTYPE_SELECTIVE_BUILD` can be set to `ON` to support this additional optimization. Currently, dtype selective build is only supported with the model API described above. Once enabled, a header file that specifies only the operators and dtypes used by the model is created and linked against a rebuild of the `portable_kernels` lib. This feature is only supported for the portable kernels library; it's not supported for optimized, quantized or custom kernel libraries.
 
-Or
+## Example Walkthrough
 
-```
-cmake -D… -DSELECT_OPS_YAML=ON
-```
+In [CMakeLists.txt](https://github.com/BujSet/executorch/blob/main/examples/selective_build/CMakeLists.txt#L48-L72), we have the following cmake config options:
+
+1. `EXECUTORCH_SELECT_OPS_YAML`
+2. `EXECUTORCH_SELECT_OPS_LIST`
+3. `EXECUTORCH_SELECT_ALL_OPS`
+4. `EXECUTORCH_SELECT_OPS_FROM_MODEL`
+5. `EXECUTORCH_DTYPE_SELECTIVE_BUILD`
+
+These options allow a user to tailor the cmake build process to utilize the different APIs, and results in different invocations on the `gen_selected_ops` [function](https://github.com/BujSet/executorch/blob/main/examples/selective_build/CMakeLists.txt#L110-L123). The following table describes some examples of how the invocation changes when these configs are set:
 
-To select from either an operator name list or a schema yaml from kernel library.
+| Example cmake Call | Resultant `gen_selected_ops` Invocation |
+| :----: | :---:| 
+|<code><br>  cmake -D… -DSELECT_OPS_LIST="aten::add.out,aten::mm.out" <br></code> | <code><br>  gen_selected_ops("" "${SELECT_OPS_LIST}" "" "" "") <br></code> |
+|<code><br> cmake -D… -DSELECT_OPS_YAML=ON <br></code> | <code><br>  set(_custom_ops_yaml ${EXECUTORCH_ROOT}/examples/portable/custom_ops/custom_ops.yaml) <br> gen_selected_ops("${_custom_ops_yaml}" "" "") <br></code> |
+|<code><br> cmake -D… -DEXECUTORCH_SELECT_OPS_FROM_MODEL="model.pte.out" <br></code> | <code><br> gen_selected_ops("" "" "" "${_model_path}" "") <br></code> |
+|<code><br> cmake -D… -DEXECUTORCH_SELECT_OPS_FROM_MODEL="model.pte.out" -DEXECUTORCH_DTYPE_SELECTIVE_BUILD=ON<br></code> | <code><br> gen_selected_ops("" "" "" "${_model_path}" "ON") <br></code> |
diff --git a/docs/source/llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md b/docs/source/llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md
index 3dbba3ef5bb..096e1004ae9 100644
--- a/docs/source/llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md
+++ b/docs/source/llm/build-run-llama3-qualcomm-ai-engine-direct-backend.md
@@ -13,12 +13,12 @@ This tutorial demonstrates how to export Llama 3 8B Instruct for Qualcomm AI Eng
 
 ## Instructions
 
-### Step1: Prepare the checkpoint of the model and optimized matrix from [Spin Quant](https://github.com/facebookresearch/SpinQuant)
+### Step 1: Prepare the checkpoint of the model and optimized matrix from [Spin Quant](https://github.com/facebookresearch/SpinQuant)
 
 1. For Llama 3 tokenizer and checkpoint, please refer to https://github.com/meta-llama/llama-models/blob/main/README.md for further instructions on how to download `tokenizer.model`, `consolidated.00.pth` and `params.json`.
 2. To get the optimized matrix, please refer to [SpinQuant on GitHub](https://github.com/facebookresearch/SpinQuant). You can download the optimized rotation matrices in the Quantized Models section. Please choose **LLaMA-3-8B/8B_W4A16KV16_lr_1.5_seed_0**.
 
-### Step2: Export to ExecuTorch with Qualcomm AI Engine Direct Backend
+### Step 2: Export to ExecuTorch with Qualcomm AI Engine Direct Backend
 Deploying large language models like Llama 3 on-device presents the following challenges:
 
 1. The model size is too large to fit in device memory for inference.
@@ -26,24 +26,44 @@ Deploying large language models like Llama 3 on-device presents the following ch
 3. Difficulty in quantization.
 
 To address these challenges, we have implemented the following solutions:
-1. Using `--pt2e_quantize qnn_16a4w` to quantize activations and weights, thereby reducing the on-disk model size and alleviating memory pressure during inference.
-2. Using `--num_sharding 8` to shard the model into sub-parts.
+1. Using `quantization.pt2e_quantize = "qnn_16a4w'` to quantize activations and weights, thereby reducing the on-disk model size and alleviating memory pressure during inference.
+2. Using `backed.qnn.num_sharding = 8` to shard the model into sub-parts.
 3. Performing graph transformations to convert or decompose operations into more accelerator-friendly operations.
-4. Using `--optimized_rotation_path <path_to_optimized_matrix>` to apply R1 and R2 of [Spin Quant](https://github.com/facebookresearch/SpinQuant) to improve accuracy.
-5. Using `--calibration_data "<|start_header_id|>system<|end_header_id|..."` to ensure that during the quantization of Llama 3 8B instruct, the calibration includes special tokens in the prompt template. For more details on the prompt template, refer to [the model card of meta llama3 instruct](https://llama.meta.com/docs/model-cards-and-prompt-formats/meta-llama-3/).
+4. Using `backend.qnn.optimized_rotation_path = "<path_to_optimized_matrix>"` to apply R1 and R2 of [Spin Quant](https://github.com/facebookresearch/SpinQuant) to improve accuracy.
+5. Using `quantization.calibration_data = "<|start_header_id|>system<|end_header_id|..."` to ensure that during quantization, the calibration includes special tokens in the prompt template. For more details on the prompt template, refer to [the model card](https://llama.meta.com/docs/model-cards-and-prompt-formats/meta-llama-3/).
 
-To export Llama 3 8B instruct with the Qualcomm AI Engine Direct Backend, ensure the following:
+To export with the Qualcomm AI Engine Direct Backend, ensure the following:
 
 1. The host machine has more than 100GB of memory (RAM + swap space).
 2. The entire process takes a few hours.
 
 ```bash
-# Please note that calibration_data must include the prompt template for special tokens.
-python -m examples.models.llama.export_llama -t <path_to_tokenizer.model>
-llama3/Meta-Llama-3-8B-Instruct/tokenizer.model -p <path_to_params.json> -c <path_to_checkpoint_for_Meta-Llama-3-8B-Instruct>  --use_kv_cache  --qnn --pt2e_quantize qnn_16a4w --disable_dynamic_shape --num_sharding 8 --calibration_tasks wikitext --calibration_limit 1 --calibration_seq_length 128 --optimized_rotation_path <path_to_optimized_matrix> --calibration_data "<|start_header_id|>system<|end_header_id|>\n\nYou are a funny chatbot.<|eot_id|><|start_header_id|>user<|end_header_id|>\n\nCould you tell me about Facebook?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"
+# path/to/config.yaml
+base:
+  model_class: llama3
+  checkpoint: path/to/consolidated.00.pth
+  params: path/to/params.json
+  tokenizer_path: path/to/tokenizer.model
+  metadata: '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}'
+model:
+  use_kv_cache: True
+  enable_dynamic_shape: False
+quantization:
+  pt2e_quantize: qnn_16a4w
+  # Please note that calibration_data must include the prompt template for special tokens.
+  calibration_data: "<|start_header_id|>system<|end_header_id|>\n\nYou are a funny chatbot.<|eot_id|><|start_header_id|>user<|end_header_id|>\n\nCould you tell me about Facebook?<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n\n"
+backend:
+  qnn:
+    enabled: True
+    num_sharding: 8
+    
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
 ```
 
-### Step3: Invoke the Runtime on an Android smartphone with Qualcomm SoCs
+### Step 3: Invoke the Runtime on an Android smartphone with Qualcomm SoCs
 1. Build executorch with Qualcomm AI Engine Direct Backend for android
     ```bash
     cmake \
@@ -116,9 +136,9 @@ You should see the message:
 ```
 
 ## What is coming?
-- Improve the performance for Llama 3 Instruct
+- Performance improvements
 - Reduce the memory pressure during inference to support 12GB Qualcomm devices
-- Support more LLMs
+- Support more LLMs (Qwen, Phi-4-mini, etc.)
 
 ## FAQ
 
diff --git a/docs/source/llm/export-custom-llm.md b/docs/source/llm/export-custom-llm.md
new file mode 100644
index 00000000000..bbdf596d21b
--- /dev/null
+++ b/docs/source/llm/export-custom-llm.md
@@ -0,0 +1,344 @@
+# Exporting custom LLMs
+
+If you have your own PyTorch model that is an LLM, this guide will show you how to manually export and lower to ExecuTorch, with many of the same optimizations as covered in the previous `export_llm` guide.
+
+This example uses Karpathy’s [nanoGPT](https://github.com/karpathy/nanoGPT), which is a minimal implementation of
+GPT-2 124M. This guide is applicable to other language models, as ExecuTorch is model-invariant.
+
+
+## Exporting to ExecuTorch (basic)
+
+Exporting takes a PyTorch model and converts it into a format that can run efficiently on consumer devices.
+
+For this example, you will need the nanoGPT model and the corresponding tokenizer vocabulary.
+
+::::{tab-set}
+:::{tab-item} curl
+```
+curl https://raw.githubusercontent.com/karpathy/nanoGPT/master/model.py -O
+curl https://huggingface.co/openai-community/gpt2/resolve/main/vocab.json -O
+```
+:::
+:::{tab-item} wget
+```
+wget https://raw.githubusercontent.com/karpathy/nanoGPT/master/model.py
+wget https://huggingface.co/openai-community/gpt2/resolve/main/vocab.json
+```
+:::
+::::
+
+To convert the model into a format optimized for standalone execution, there are two steps. First, use the PyTorch
+`export` function to convert the PyTorch model into an intermediate, platform-independent intermediate representation. Then
+use the ExecuTorch `to_edge` and `to_executorch` methods to prepare the model for on-device execution. This creates a .pte
+file which can be loaded by a desktop or mobile application at runtime.
+
+Create a file called export_nanogpt.py with the following contents:
+
+```python
+# export_nanogpt.py
+
+import torch
+
+from executorch.exir import EdgeCompileConfig, to_edge
+from torch.nn.attention import sdpa_kernel, SDPBackend
+from torch.export import export, export_for_training
+
+from model import GPT
+
+# Load the model.
+model = GPT.from_pretrained('gpt2')
+
+# Create example inputs. This is used in the export process to provide
+# hints on the expected shape of the model input.
+example_inputs = (torch.randint(0, 100, (1, model.config.block_size), dtype=torch.long), )
+
+# Set up dynamic shape configuration. This allows the sizes of the input tensors
+# to differ from the sizes of the tensors in `example_inputs` during runtime, as
+# long as they adhere to the rules specified in the dynamic shape configuration.
+# Here we set the range of 0th model input's 1st dimension as
+# [0, model.config.block_size].
+# See https://pytorch.org/executorch/main/concepts#dynamic-shapes
+# for details about creating dynamic shapes.
+dynamic_shape = (
+    {1: torch.export.Dim("token_dim", max=model.config.block_size)},
+)
+
+# Trace the model, converting it to a portable intermediate representation.
+# The torch.no_grad() call tells PyTorch to exclude training-specific logic.
+with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad():
+    m = export_for_training(model, example_inputs, dynamic_shapes=dynamic_shape).module()
+    traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape)
+
+# Convert the model into a runnable ExecuTorch program.
+edge_config = EdgeCompileConfig(_check_ir_validity=False)
+edge_manager = to_edge(traced_model,  compile_config=edge_config)
+et_program = edge_manager.to_executorch()
+
+# Save the ExecuTorch program to a file.
+with open("nanogpt.pte", "wb") as file:
+    file.write(et_program.buffer)
+```
+
+To export, run the script with `python export_nanogpt.py` (or python3, as appropriate for your environment). It will generate a `nanogpt.pte` file in the current directory.
+
+For more information, see [Exporting to ExecuTorch](https://pytorch.org/executorch/main/tutorials/export-to-executorch-tutorial) and
+[torch.export](https://pytorch.org/docs/stable/export.html).
+
+## Backend delegation
+
+While ExecuTorch provides a portable, cross-platform implementation for all
+operators, it also provides specialized backends for a number of different
+targets. These include, but are not limited to, x86 and ARM CPU acceleration via
+the XNNPACK backend, Apple acceleration via the Core ML backend and Metal
+Performance Shader (MPS) backend, and GPU acceleration via the Vulkan backend.
+
+Because optimizations are specific to a given backend, each pte file is specific
+to the backend(s) targeted at export. To support multiple devices, such as
+XNNPACK acceleration for Android and Core ML for iOS, export a separate PTE file
+for each backend.
+
+To delegate a model to a specific backend during export, ExecuTorch uses the
+`to_edge_transform_and_lower()` function. This function takes the exported program
+from `torch.export` and a backend-specific partitioner object. The partitioner
+identifies parts of the computation graph that can be optimized by the target
+backend. Within `to_edge_transform_and_lower()`, the exported program is
+converted to an edge dialect program. The partitioner then delegates compatible
+graph sections to the backend for acceleration and optimization. Any graph parts
+not delegated are executed by ExecuTorch's default operator implementations.
+
+To delegate the exported model to a specific backend, we need to import its
+partitioner as well as edge compile config from ExecuTorch codebase first, then
+call `to_edge_transform_and_lower`.
+
+Here's an example of how to delegate nanoGPT to XNNPACK (if you're deploying to an Android phone for instance):
+
+```python
+# export_nanogpt.py
+
+# Load partitioner for Xnnpack backend
+from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
+
+# Model to be delegated to specific backend should use specific edge compile config
+from executorch.backends.xnnpack.utils.configs import get_xnnpack_edge_compile_config
+from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower
+
+import torch
+from torch.export import export
+from torch.nn.attention import sdpa_kernel, SDPBackend
+from torch.export import export_for_training
+
+from model import GPT
+
+# Load the nanoGPT model.
+model = GPT.from_pretrained('gpt2')
+
+# Create example inputs. This is used in the export process to provide
+# hints on the expected shape of the model input.
+example_inputs = (
+        torch.randint(0, 100, (1, model.config.block_size - 1), dtype=torch.long),
+    )
+
+# Set up dynamic shape configuration. This allows the sizes of the input tensors
+# to differ from the sizes of the tensors in `example_inputs` during runtime, as
+# long as they adhere to the rules specified in the dynamic shape configuration.
+# Here we set the range of 0th model input's 1st dimension as
+# [0, model.config.block_size].
+# See https://pytorch.org/executorch/main/concepts.html#dynamic-shapes
+# for details about creating dynamic shapes.
+dynamic_shape = (
+    {1: torch.export.Dim("token_dim", max=model.config.block_size - 1)},
+)
+
+# Trace the model, converting it to a portable intermediate representation.
+# The torch.no_grad() call tells PyTorch to exclude training-specific logic.
+with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad():
+    m = export_for_training(model, example_inputs, dynamic_shapes=dynamic_shape).module()
+    traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape)
+
+# Convert the model into a runnable ExecuTorch program.
+# To be further lowered to Xnnpack backend, `traced_model` needs xnnpack-specific edge compile config
+edge_config = get_xnnpack_edge_compile_config()
+# Converted to edge program and then delegate exported model to Xnnpack backend
+# by invoking `to` function with Xnnpack partitioner.
+edge_manager = to_edge_transform_and_lower(traced_model, partitioner = [XnnpackPartitioner()], compile_config = edge_config)
+et_program = edge_manager.to_executorch()
+
+# Save the Xnnpack-delegated ExecuTorch program to a file.
+with open("nanogpt.pte", "wb") as file:
+    file.write(et_program.buffer)
+```
+
+
+## Quantization
+
+Quantization refers to a set of techniques for running calculations and storing tensors using lower precision types.
+Compared to 32-bit floating point, using 8-bit integers can provide both a significant speedup and reduction in
+memory usage. There are many approaches to quantizing a model, varying in amount of pre-processing required, data
+types used, and impact on model accuracy and performance.
+
+Because compute and memory are highly constrained on mobile devices, some form of quantization is necessary to ship
+large models on consumer electronics. In particular, large language models, such as Llama2, may require quantizing
+model weights to 4 bits or less.
+
+Leveraging quantization requires transforming the model before export. PyTorch provides the pt2e (PyTorch 2 Export)
+API for this purpose. This example targets CPU acceleration using the XNNPACK delegate. As such, it needs to use the
+ XNNPACK-specific quantizer. Targeting a different backend will require use of the corresponding quantizer.
+
+To use 8-bit integer dynamic quantization with the XNNPACK delegate, call `prepare_pt2e`, calibrate the model by
+running with a representative input, and then call `convert_pt2e`. This updates the computational graph to use
+quantized operators where available.
+
+```python
+# export_nanogpt.py
+
+from executorch.backends.transforms.duplicate_dynamic_quant_chain import (
+    DuplicateDynamicQuantChainPass,
+)
+from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
+    get_symmetric_quantization_config,
+    XNNPACKQuantizer,
+)
+from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
+```
+
+```python
+# Use dynamic, per-channel quantization.
+xnnpack_quant_config = get_symmetric_quantization_config(
+    is_per_channel=True, is_dynamic=True
+)
+xnnpack_quantizer = XNNPACKQuantizer()
+xnnpack_quantizer.set_global(xnnpack_quant_config)
+
+m = export_for_training(model, example_inputs).module()
+
+# Annotate the model for quantization. This prepares the model for calibration.
+m = prepare_pt2e(m, xnnpack_quantizer)
+
+# Calibrate the model using representative inputs. This allows the quantization
+# logic to determine the expected range of values in each tensor.
+m(*example_inputs)
+
+# Perform the actual quantization.
+m = convert_pt2e(m, fold_quantize=False)
+DuplicateDynamicQuantChainPass()(m)
+
+traced_model = export(m, example_inputs)
+```
+
+Additionally, add or update the `to_edge_transform_and_lower()` call to use `XnnpackPartitioner`. This
+instructs ExecuTorch to optimize the model for CPU execution via the XNNPACK backend.
+
+```python
+from executorch.backends.xnnpack.partition.xnnpack_partitioner import (
+    XnnpackPartitioner,
+)
+```
+
+```python
+edge_config = get_xnnpack_edge_compile_config()
+# Convert to edge dialect and lower to XNNPack.
+edge_manager = to_edge_transform_and_lower(traced_model, partitioner = [XnnpackPartitioner()], compile_config = edge_config)
+et_program = edge_manager.to_executorch()
+
+with open("nanogpt.pte", "wb") as file:
+    file.write(et_program.buffer)
+```
+
+For more information, see [Quantization in ExecuTorch](../quantization-overview.md).
+
+## Profiling and Debugging
+After lowering a model by calling `to_edge_transform_and_lower()`, you may want to see what got delegated and what didn’t. ExecuTorch
+provides utility methods to give insight on the delegation. You can use this information to gain visibility into
+the underlying computation and diagnose potential performance issues. Model authors can use this information to
+structure the model in a way that is compatible with the target backend.
+
+### Visualizing the Delegation
+
+The `get_delegation_info()` method provides a summary of what happened to the model after the `to_edge_transform_and_lower()` call:
+
+```python
+from executorch.devtools.backend_debug import get_delegation_info
+from tabulate import tabulate
+
+# ... After call to to_edge_transform_and_lower(), but before to_executorch()
+graph_module = edge_manager.exported_program().graph_module
+delegation_info = get_delegation_info(graph_module)
+print(delegation_info.get_summary())
+df = delegation_info.get_operator_delegation_dataframe()
+print(tabulate(df, headers="keys", tablefmt="fancy_grid"))
+```
+
+For nanoGPT targeting the XNNPACK backend, you might see the following (note that the numbers below are for illustration purposes only and actual values may vary):
+```
+Total  delegated  subgraphs:  145
+Number  of  delegated  nodes:  350
+Number  of  non-delegated  nodes:  760
+```
+
+
+|    |  op_type                                 |  # in_delegated_graphs  |  # in_non_delegated_graphs  |
+|----|---------------------------------|------- |-----|
+|  0  |  aten__softmax_default  |  12  |  0  |
+|  1  |  aten_add_tensor  |  37  |  0  |
+|  2  |  aten_addmm_default  |  48  |  0  |
+|  3  |  aten_any_dim  |  0  |  12  |
+|      |  ...  |    |    |
+|  25  |  aten_view_copy_default  |  96  |  122  |
+|      |  ...  |    |    |
+|  30  |  Total  |  350  |  760  |
+
+From the table, the operator `aten_view_copy_default` appears 96 times in delegate graphs and 122 times in non-delegated graphs.
+To see a more detailed view, use the `format_delegated_graph()` method to get a formatted str of printout of the whole graph or use `print_delegated_graph()` to print directly:
+
+```python
+from executorch.exir.backend.utils import format_delegated_graph
+graph_module = edge_manager.exported_program().graph_module
+print(format_delegated_graph(graph_module))
+```
+This may generate a large amount of output for large models. Consider using "Control+F" or "Command+F" to locate the operator you’re interested in
+(e.g. “aten_view_copy_default”). Observe which instances are not under lowered graphs.
+
+In the fragment of the output for nanoGPT below, observe that a transformer module has been delegated to XNNPACK while the where operator is not.
+
+```
+%aten_where_self_22 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.where.self](args = (%aten_logical_not_default_33, %scalar_tensor_23, %scalar_tensor_22), kwargs = {})
+%lowered_module_144 : [num_users=1] = get_attr[target=lowered_module_144]
+backend_id: XnnpackBackend
+lowered graph():
+    %p_transformer_h_0_attn_c_attn_weight : [num_users=1] = placeholder[target=p_transformer_h_0_attn_c_attn_weight]
+    %p_transformer_h_0_attn_c_attn_bias : [num_users=1] = placeholder[target=p_transformer_h_0_attn_c_attn_bias]
+    %getitem : [num_users=1] = placeholder[target=getitem]
+    %sym_size : [num_users=2] = placeholder[target=sym_size]
+    %aten_view_copy_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.view_copy.default](args = (%getitem, [%sym_size, 768]), kwargs = {})
+    %aten_permute_copy_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.permute_copy.default](args = (%p_transformer_h_0_attn_c_attn_weight, [1, 0]), kwargs = {})
+    %aten_addmm_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.addmm.default](args = (%p_transformer_h_0_attn_c_attn_bias, %aten_view_copy_default, %aten_permute_copy_default), kwargs = {})
+    %aten_view_copy_default_1 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.view_copy.default](args = (%aten_addmm_default, [1, %sym_size, 2304]), kwargs = {})
+    return [aten_view_copy_default_1]
+```
+
+### Further Model Analysis and Debugging
+
+Through the [ExecuTorch's Developer Tools](getting-started.md#performance-analysis), users are able to profile model execution, giving timing information for each operator in the model, doing model numeric debugging, etc.
+
+An ETRecord is an artifact generated at the time of export that contains model graphs and source-level metadata linking the ExecuTorch program to the original PyTorch model. You can view all profiling events without an ETRecord, though with an ETRecord, you will also be able to link each event to the types of operators being executed, module hierarchy, and stack traces of the original PyTorch source code. For more information, see [the ETRecord docs](../etrecord.rst).
+
+In your export script, after calling `to_edge()` and `to_executorch()`, call `generate_etrecord()` with the `EdgeProgramManager` from `to_edge()` and the `ExecuTorchProgramManager` from `to_executorch()`. Make sure to copy the `EdgeProgramManager`, as the call to `to_edge_transform_and_lower()` mutates the graph in-place.
+
+```
+# export_nanogpt.py
+
+import copy
+from executorch.devtools import generate_etrecord
+
+# Make the deep copy immediately after to to_edge()
+edge_manager_copy = copy.deepcopy(edge_manager)
+
+# ...
+# Generate ETRecord right after to_executorch()
+etrecord_path = "etrecord.bin"
+generate_etrecord(etrecord_path, edge_manager_copy, et_program)
+```
+
+Run the export script and the ETRecord will be generated as `etrecord.bin`.
+
+To learn more about ExecuTorch's Developer Tools, see the [Introduction to the ExecuTorch Developer Tools](../devtools-overview.md).
diff --git a/docs/source/llm/export-llm.md b/docs/source/llm/export-llm.md
new file mode 100644
index 00000000000..462d9a51849
--- /dev/null
+++ b/docs/source/llm/export-llm.md
@@ -0,0 +1,228 @@
+# Exporting LLMs
+
+Instead of needing to manually write code to call torch.export(), use ExecuTorch's assortment of lowering APIs, or even interact with TorchAO quantize_ APIs for quantization, we have provided an out of box experience which performantly exports a selection of supported models to ExecuTorch.
+
+## Prerequisites
+
+The LLM export functionality requires the `pytorch_tokenizers` package. If you encounter a `ModuleNotFoundError: No module named 'pytorch_tokenizers'` error, install it from the ExecutorTorch source code:
+
+```bash
+pip install -e ./extension/llm/tokenizers/
+```
+
+## Supported Models
+
+As of this doc, the list of supported LLMs include the following:
+- Llama 2/3/3.1/3.2
+- Qwen 2.5/3
+- Phi 3.5/4-mini
+- SmolLM2
+
+The up-to-date list of supported LLMs can be found in the code [here](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L32).
+
+## The export_llm API
+`export_llm` is ExecuTorch's high-level export API for LLMs. In this tutorial, we will focus on exporting Llama 3.2 1B using this API. `export_llm`'s arguments are specified either through CLI args or through a yaml configuration whose fields are defined in [`LlmConfig`](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py). To call `export_llm`:
+
+```
+python -m executorch.examples.extension.llm.export.export_llm
+  --config <path-to-config-yaml>
+  +base.<additional-CLI-overrides>
+```
+
+## Basic export
+
+To perform a basic export of Llama3.2, we will first need to download the checkpoint file (`consolidated.00.pth`) and params file (`params.json`). You can find these from the [Llama website](https://www.llama.com/llama-downloads/) or [Hugging Face](https://huggingface.co/meta-llama/Llama-3.2-1B/tree/main/original).
+
+Then, we specify the `model_class`, `checkpoint` (path to checkpoint file), and `params` (path to params file) as arguments. Additionally, later when we run the exported .pte with our runner APIs, the runner will need to know about the bos and eos ids for this model to know when to terminate. These are exposed through bos and eos getter methods in the .pte, which we can add by specifying bos and eos ids in a `metadata` argument. The values for these tokens can usually be found in the model's `tokenizer_config.json` on HuggingFace.
+
+```
+# path/to/config.yaml
+base:
+  model_class: llama3_2
+  checkpoint: path/to/consolidated.00.pth
+  params: path/to/params.json
+  metadata: '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}'
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+We only require manually specifying a checkpoint path for the Llama model family, since it is our most optimized model and we have more advanced optimizations such as [SpinQuant](https://github.com/pytorch/executorch/blob/main/examples/models/llama/README.md#spinquant) that require custom checkpoints.
+
+For the other supported LLMs, the checkpoint will be downloaded from HuggingFace automatically, and the param files can be found in their respective directories under `executorch/examples/models`, for instance `executorch/examples/models/qwen3/config/0_6b_config.json`.
+
+## Export settings
+[ExportConfig](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py) contains settings for the exported `.pte`, such as `max_seq_length` (max length of the prompt) and `max_context_length` (max length of the model's memory/cache).
+
+## Adding optimizations
+`export_llm` performs a variety of optimizations to the model before export, during export, and during lowering. Quantization and delegation to accelerator backends are the main ones and will be covered in the next two sections. All other optimizations can be found under [`ModelConfig`](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L120). We will go ahead and add a few optimizations.
+
+```
+# path/to/config.yaml
+base:
+  model_class: llama3_2
+  checkpoint: path/to/consolidated.00.pth
+  params: path/to/params.json
+  metadata: '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}'
+model:
+  use_kv_cache: True
+  use_sdpa_with_kv_cache: True
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+`use_kv_cache` and `use_sdpa_with_kv_cache` are recommended to export any LLM, while other options are useful situationally. For example:
+- `use_shared_embedding` can help for models with tied input/output embedding layers, given that you quantize using TorchAO low bit ops (`quantization.qmode: torchao:8da(\\d+)w` or `quantization.qmode: torchao:fpa(\d+)w`), see more [here](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L307).
+- `use_attention_sink` to extend generation by removing from the beginning of the KV cache when the max context length is reached.
+- `quantize_kv_cache` quantizes the KV cache in int8.
+- `local_global_attention` impements [Local-Global Attention](https://arxiv.org/abs/2411.09604), making specific attention layers use a much smaller localized sliding window KV cache.
+
+## Quantization
+Quantization options are defined by [`QuantizationConfig`](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L283). ExecuTorch does quantization in two ways:
+1. TorchAO [`quantize_`](https://docs.pytorch.org/ao/stable/generated/torchao.quantization.quantize_.html) API
+2. [pt2e quantization](https://docs.pytorch.org/ao/main/tutorials_source/pt2e_quant_ptq.html)
+
+### TorchAO (XNNPACK)
+TorchAO quantizes at the source code level, swapping out Linear modules for QuantizedLinear modules.
+**To quantize on XNNPACK backend, this is the quantization path to follow.**
+The quantization modes are defined [here](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L306).
+
+Common ones to use are:
+- `8da4w`: short for int8 dynamic activation + int4 weight quantization.
+- `int8`: int8 weight-only quanziation.
+
+Group size is specified with:
+- `group_size`: 8, 32, 64, etc.
+
+For Arm CPUs, there are also [low-bit kernels](https://pytorch.org/blog/hi-po-low-bit-operators/) for int8 dynamic activation + int[1-8] weight quantization. Note that this should not be used alongside XNNPACK, and experimentally we have found that the performance could sometimes even be better for the equivalent `8da4w`. To use these, specify `qmode` to either:
+- `torchao:8da(\d+)w`: int8 dynamic activation + int[1-8] weights, for example `torchao:8da5w`
+- `torchao:fpa(\d+)w`: int[1-8] weight only, for example `torchao:fpa4w`
+
+To quantize embeddings, specify either `embedding_quantize: <bitwidth>,<groupsize>` (`bitwidth` here must be 2, 4, or 8), or for low-bit kernels use `embedding_quantize: torchao:<bitwidth>,<groupsize>` (`bitwidth` can be from 1-8).
+
+```
+# path/to/config.yaml
+base:
+  model_class: llama3_2
+  checkpoint: path/to/consolidated.00.pth
+  params: path/to/params.json
+  metadata: '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}'
+model:
+  use_kv_cache: True
+  use_sdpa_withp_kv_cache: True
+quantization:
+  embedding_quantize: 4,32
+  qmode: 8da4w
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+### pt2e (QNN, CoreML, and Vulkan)
+pt2e quantizes at the post-export graph level, swapping nodes and injecting quant/dequant nodes.
+**To quantize on non-CPU backends (QNN, CoreML, Vulkan), this is the quantization path to follow.**
+Read more about pt2e [here](https://docs.pytorch.org/ao/main/tutorials_source/pt2e_quant_ptq.html), and how ExecuTorch uses pt2e [here](https://github.com/pytorch/executorch/blob/main/docs/source/quantization-overview.md).
+
+*CoreML and Vulkan support for export_llm is currently experimental and limited. To read more about QNN export, please read [Running on Android (Qualcomm)](build-run-llama3-qualcomm-ai-engine-direct-backend.md).*
+
+
+## Backend support
+Backend options are defined by [`BackendConfig`](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L434). Each backend has their own backend configuration options. Here is an example of lowering the LLM to XNNPACK for CPU acceleration:
+
+```
+# path/to/config.yaml
+base:
+  model_class: llama3_2
+  checkpoint: path/to/consolidated.00.pth
+  params: path/to/params.json
+  metadata: '{"get_bos_id":128000, "get_eos_ids":[128009, 128001]}'
+model:
+  use_kv_cache: True
+  use_sdpa_withp_kv_cache: True
+quantization:
+  embedding_quantize: 4,32
+  qmode: 8da4w
+backend:
+  xnnpack:
+    enabled: True
+    extended_ops: True  # Expand the selection of ops delegated to XNNPACK.
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+
+## Profiling and Debugging
+To see which ops got delegated to the backend and which didn't, specify `verbose: True`:
+
+```
+# path/to/config.yaml
+...
+debug:
+  verbose: True
+...
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+In the logs, there will be a table of all ops in the graph, and which ones were and were not delegated.
+
+Here is an example:
+<details>
+<summary>Click to see delegation details</summary>
+
+Total delegated subgraphs: 368 <br/>
+Number of delegated nodes: 2588 <br/>
+Number of non-delegated nodes: 2513 <br/>
+
+
+|    |  op_type                                 |  # in_delegated_graphs  |  # in_non_delegated_graphs  |
+|----|---------------------------------|------- |-----|
+|  0  |  _assert_scalar  |  0  |  167  |
+|  1  |  _local_scalar_dense  |  0  |  123  |
+|  2  |  add  |  0  |  31  |
+|  3  |  aten__to_copy_default  |  0  |  44  |
+|  4  |  aten_add_tensor  |  418  |  44  |
+|  5  |  aten_alias_copy_default  |  0  |  52  |
+|      |  ...  |    |    |
+|  15  |  aten_linear_default  |  183  |  0  |
+|  18  |  aten_mul_tensor  |  445  |  0  |
+|  20  |  aten_pow_tensor_scalar  |  157  |  0  |
+|  22  |  aten_rsqrt_default  |  157  |  0  |
+|  27  |  aten_view_copy_default  |  0  |  126  |
+|  31  |  getitem  |  366  |  628  |
+|      |  ...  |    |    |
+|  41  |  torchao_quantize_affine_default  |  183  |  0  |
+|  42  |  Total  |  2588  |  2513  |
+
+</details>
+<br/>
+
+To do further performance analysis, you can may opt to use [ExecuTorch's Developer Tools](getting-started.md#performance-analysis) to do things such as trace individual operator performance back to source code, view memory planning, and debug intermediate activations. To generate the ETRecord to link back `.pte` program to source code, you can use:
+
+```
+# path/to/config.yaml
+...
+debug:
+  generate_etrecord: True
+...
+
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml
+```
+
+Other debug and profiling options can be found in [DebugConfig](https://github.com/pytorch/executorch/blob/main/extension/llm/export/config/llm_config.py#L228).
+
+A few examples ones:
+- `profile_memory`: Used to generate activation memory profile in chrome trace format. It allows one to visualize the lifetimes of different intermediate tensors of a model, how their lifetimes overlap, where these tensors come from, and how they impact the  memory footprint of the model during its execution. Click [here](https://github.com/pytorch/executorch/blob/dd4488d720d676a1227450e8ea0c0c97beed900c/docs/source/memory-planning-inspection.md?plain=1#L19) for more details on memory profiling.
+- `profile_path`: Used to generate time profile of various components of export_llm. Such components include `torch.export`, quantization, `to_edge`, delegation via to_backend APIs etc. This option generate a .html file that gives you time profile in flamegraph/icicle format. It is helpful to understand what part of `export_llm` takes the most time. Largely useful for developers and contributors of ExecuTorch. For more details on flamegraph one can checkout https://www.parca.dev/docs/icicle-graph-anatomy/
+
+To learn more about ExecuTorch's Developer Tools, see the [Introduction to the ExecuTorch Developer Tools](../devtools-overview.md).
diff --git a/docs/source/llm/getting-started.md b/docs/source/llm/getting-started.md
index 7d54f4d2dde..c75d5bbc3f5 100644
--- a/docs/source/llm/getting-started.md
+++ b/docs/source/llm/getting-started.md
@@ -1,869 +1,26 @@
-# Intro to LLMs in Executorch
+# Deploying LLMs to ExecuTorch
 
-Welcome to LLM Manual! This manual is designed to provide a practical example to leverage
-ExecuTorch in onboarding your own Large Language Models (LLMs). Our primary goal is to offer
- a clear and concise guideline on how to integrate our system with your own LLMs.
-
-Please note that this project is intended as a demonstration and not as a fully functional
-example with optimal performance. As such, certain components such as the sampler, tokenizer,
-and others are provided in their bare minimum versions solely for demonstration purposes.
-Consequently, the results produced by the model may vary and might not always be optimal.
+ExecuTorch is designed to support all types of machine learning models, and LLMs are no exception.
+In this section we demonstrate how to leverage ExecuTorch to performantly run state of the art
+LLMs on-device out of the box with our provided export LLM APIs, acceleration backends, quantization
+libraries, tokenizers, and more.
 
 We encourage users to use this project as a starting point and adapt it to their specific needs,
 which includes creating your own versions of the tokenizer, sampler, acceleration backends, and
 other components. We hope this project serves as a useful guide in your journey with LLMs and ExecuTorch.
 
-For deploying Llama with optimal performance, please see [Llama guide](llama.md).
-
-### Table Of Contents
-
-
-1.  Prerequisites
-2.  Hello World Example
-3.  Quantization
-4.  Using Mobile Acceleration
-5.  Debugging and Profiling
-6.  How to use custom kernels
-7.  How to build mobile apps
-
 
 ## Prerequisites
 
-To follow this guide, you'll need to clone the ExecuTorch repository and install dependencies.
-ExecuTorch recommends Python 3.10 and the use of Conda to manage your environment. Conda is not
-required, though be aware that you may need to replace the use of python/pip with python3/pip3
-depending on your environment.
-
-::::{tab-set}
-:::{tab-item} conda
-Instructions on installing miniconda can be [found here](https://docs.anaconda.com/free/miniconda).
-
-```
-# Create a directory for this example.
-mkdir et-nanogpt
-cd et-nanogpt
-
-# Clone the ExecuTorch repository.
-mkdir third-party
-git clone -b viable/strict https://github.com/pytorch/executorch.git third-party/executorch && cd third-party/executorch
-
-# Create either a Python virtual environment:
-python3 -m venv .venv && source .venv/bin/activate && pip install --upgrade pip
-
-# Or a Conda environment:
-conda create -yn executorch python=3.10.0 && conda activate executorch
-
-# Install requirements
-./install_executorch.sh
-
-cd ../..
-```
-:::
-:::{tab-item} pyenv-virtualenv
-Instructions on installing pyenv-virtualenv can be [found here](https://github.com/pyenv/pyenv-virtualenv?tab=readme-ov-file#installing-with-homebrew-for-macos-users).
-
-Importantly, if installing pyenv through brew, it does not automatically enable pyenv in the terminal, leading to errors. Run the following commands to enable.
-See the pyenv-virtualenv installation guide above on how to add this to your .bashrc or .zshrc to avoid needing to run these commands manually.
-```
-eval "$(pyenv init -)"
-eval "$(pyenv virtualenv-init -)"
-```
-
-```
-# Create a directory for this example.
-mkdir et-nanogpt
-cd et-nanogpt
-
-pyenv install -s 3.10
-pyenv virtualenv 3.10 executorch
-pyenv activate executorch
-
-# Clone the ExecuTorch repository.
-git clone -b viable/strict https://github.com/pytorch/executorch.git third-party/executorch && cd third-party/executorch
-
-# Install requirements.
-PYTHON_EXECUTABLE=python ./install_executorch.sh
-
-cd ../..
-```
-:::
-::::
-
-For more information, see [Setting Up ExecuTorch](../getting-started-setup.rst).
-
-
-## Running a Large Language Model Locally
-
-This example uses Karpathy’s [nanoGPT](https://github.com/karpathy/nanoGPT), which is a minimal implementation of
-GPT-2 124M. This guide is applicable to other language models, as ExecuTorch is model-invariant.
-
-There are two steps to running a model with ExecuTorch:
-
-1.  Export the model. This step preprocesses it into a format suitable for runtime execution.
-2.  At runtime, load the model file and run with the ExecuTorch runtime.
-
-<br />
-
-The export step happens ahead of time, typically as part of the application build or when the model changes. The resultant
-.pte file is distributed with the application. At runtime, the application loads the .pte file and passes it to the
-ExecuTorch runtime.
-
-### Step 1. Exporting to ExecuTorch
-
-Exporting takes a PyTorch model and converts it into a format that can run efficiently on consumer devices.
-
-For this example, you will need the nanoGPT model and the corresponding tokenizer vocabulary.
-
-::::{tab-set}
-:::{tab-item} curl
-```
-curl https://raw.githubusercontent.com/karpathy/nanoGPT/master/model.py -O
-curl https://huggingface.co/openai-community/gpt2/resolve/main/vocab.json -O
-```
-:::
-:::{tab-item} wget
-```
-wget https://raw.githubusercontent.com/karpathy/nanoGPT/master/model.py
-wget https://huggingface.co/openai-community/gpt2/resolve/main/vocab.json
-```
-:::
-::::
-
-To convert the model into a format optimized for standalone execution, there are two steps. First, use the PyTorch
-`export` function to convert the PyTorch model into an intermediate, platform-independent intermediate representation. Then
-use the ExecuTorch `to_edge` and `to_executorch` methods to prepare the model for on-device execution. This creates a .pte
-file which can be loaded by a desktop or mobile application at runtime.
-
-Create a file called export_nanogpt.py with the following contents:
-
-```python
-# export_nanogpt.py
-
-import torch
-
-from executorch.exir import EdgeCompileConfig, to_edge
-from torch.nn.attention import sdpa_kernel, SDPBackend
-from torch.export import export, export_for_training
-
-from model import GPT
-
-# Load the model.
-model = GPT.from_pretrained('gpt2')
-
-# Create example inputs. This is used in the export process to provide
-# hints on the expected shape of the model input.
-example_inputs = (torch.randint(0, 100, (1, model.config.block_size), dtype=torch.long), )
-
-# Set up dynamic shape configuration. This allows the sizes of the input tensors
-# to differ from the sizes of the tensors in `example_inputs` during runtime, as
-# long as they adhere to the rules specified in the dynamic shape configuration.
-# Here we set the range of 0th model input's 1st dimension as
-# [0, model.config.block_size].
-# See https://pytorch.org/executorch/main/concepts#dynamic-shapes
-# for details about creating dynamic shapes.
-dynamic_shape = (
-    {1: torch.export.Dim("token_dim", max=model.config.block_size)},
-)
-
-# Trace the model, converting it to a portable intermediate representation.
-# The torch.no_grad() call tells PyTorch to exclude training-specific logic.
-with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad():
-    m = export_for_training(model, example_inputs, dynamic_shapes=dynamic_shape).module()
-    traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape)
-
-# Convert the model into a runnable ExecuTorch program.
-edge_config = EdgeCompileConfig(_check_ir_validity=False)
-edge_manager = to_edge(traced_model,  compile_config=edge_config)
-et_program = edge_manager.to_executorch()
-
-# Save the ExecuTorch program to a file.
-with open("nanogpt.pte", "wb") as file:
-    file.write(et_program.buffer)
-```
-
-To export, run the script with `python export_nanogpt.py` (or python3, as appropriate for your environment). It will generate a `nanogpt.pte` file in the current directory.
-
-For more information, see [Exporting to ExecuTorch](https://pytorch.org/executorch/main/tutorials/export-to-executorch-tutorial) and
-[torch.export](https://pytorch.org/docs/stable/export.html).
-
-### Step 2. Invoking the Runtime
-
-ExecuTorch provides a set of runtime APIs and types to load and run models.
-
-Create a file called main.cpp with the following contents:
-
-```cpp
-// main.cpp
-
-#include <cstdint>
-
-#include "basic_sampler.h"
-#include "basic_tokenizer.h"
-
-#include <executorch/extension/module/module.h>
-#include <executorch/extension/tensor/tensor.h>
-#include <executorch/runtime/core/evalue.h>
-#include <executorch/runtime/core/exec_aten/exec_aten.h>
-#include <executorch/runtime/core/result.h>
-
-using executorch::aten::ScalarType;
-using executorch::aten::Tensor;
-using executorch::extension::from_blob;
-using executorch::extension::Module;
-using executorch::runtime::EValue;
-using executorch::runtime::Result;
-```
-
-The model inputs and outputs take the form of tensors. A tensor can be thought of as an multi-dimensional array.
-The ExecuTorch `EValue` class provides a wrapper around tensors and other ExecuTorch data types.
-
-Since the LLM generates one token at a time, the driver code needs to repeatedly invoke the model, building the
-output token by token. Each generated token is passed as input for the next run.
-
-```cpp
-// main.cpp
-
-// The value of the gpt2 `<|endoftext|>` token.
-#define ENDOFTEXT_TOKEN 50256
-
-std::string generate(
-    Module& llm_model,
-    std::string& prompt,
-    BasicTokenizer& tokenizer,
-    BasicSampler& sampler,
-    size_t max_input_length,
-    size_t max_output_length) {
-  // Convert the input text into a list of integers (tokens) that represents it,
-  // using the string-to-token mapping that the model was trained on. Each token
-  // is an integer that represents a word or part of a word.
-  std::vector<int64_t> input_tokens = tokenizer.encode(prompt);
-  std::vector<int64_t> output_tokens;
-
-  for (auto i = 0u; i < max_output_length; i++) {
-    // Convert the input_tokens from a vector of int64_t to EValue. EValue is a
-    // unified data type in the ExecuTorch runtime.
-    auto inputs = from_blob(
-        input_tokens.data(),
-        {1, static_cast<int>(input_tokens.size())},
-        ScalarType::Long);
-
-    // Run the model. It will return a tensor of logits (log-probabilities).
-    auto logits_evalue = llm_model.forward(inputs);
-
-    // Convert the output logits from EValue to std::vector, which is what the
-    // sampler expects.
-    Tensor logits_tensor = logits_evalue.get()[0].toTensor();
-    std::vector<float> logits(
-        logits_tensor.data_ptr<float>(),
-        logits_tensor.data_ptr<float>() + logits_tensor.numel());
-
-    // Sample the next token from the logits.
-    int64_t next_token = sampler.sample(logits);
-
-    // Break if we reached the end of the text.
-    if (next_token == ENDOFTEXT_TOKEN) {
-      break;
-    }
-
-    // Add the next token to the output.
-    output_tokens.push_back(next_token);
-
-    std::cout << tokenizer.decode({next_token});
-    std::cout.flush();
-
-    // Update next input.
-    input_tokens.push_back(next_token);
-    if (input_tokens.size() > max_input_length) {
-      input_tokens.erase(input_tokens.begin());
-    }
-  }
-
-  std::cout << std::endl;
-
-  // Convert the output tokens into a human-readable string.
-  std::string output_string = tokenizer.decode(output_tokens);
-  return output_string;
-}
-```
-
-The `Module` class handles loading the .pte file and preparing for execution.
-
-The tokenizer is responsible for converting from a human-readable string representation of the prompt to the
-numerical form expected by the model. To do this, the tokenzier associates short substrings with a given token ID.
-The tokens can be thought of as representing words or parts of words, though, in-practice, they may be arbitrary
-sequences of characters.
-
-The tokenizer loads the vocabulary from a file, which contains the mapping between each token ID and the text it
-represents. Call `tokenizer.encode()` and `tokenizer.decode()` to convert between string and token representations.
-
-The sampler is responsible for selecting the next token, based on the logits, or log-probabilties, output by the
-model. The LLM returns a logit value for each possible next token. The sampler chooses which token to use based
-on some strategy. The simplest approach, used here, is to take the token with the highest logit value.
-
-Samplers may provide configurable options, such as configurable amount of randomness to the outputs selection,
-penalties for repeated tokens, and biases to prioritize or de-prioritize specific tokens.
-
-
-```cpp
-// main.cpp
-
-int main() {
-  // Set up the prompt. This provides the seed text for the model to elaborate.
-  std::cout << "Enter model prompt: ";
-  std::string prompt;
-  std::getline(std::cin, prompt);
-
-  // The tokenizer is used to convert between tokens (used by the model) and
-  // human-readable strings.
-  BasicTokenizer tokenizer("vocab.json");
-
-  // The sampler is used to sample the next token from the logits.
-  BasicSampler sampler = BasicSampler();
-
-  // Load the exported nanoGPT program, which was generated via the previous
-  // steps.
-  Module model("nanogpt.pte", Module::LoadMode::MmapUseMlockIgnoreErrors);
-
-  const auto max_input_tokens = 1024;
-  const auto max_output_tokens = 30;
-  std::cout << prompt;
-  generate(
-      model, prompt, tokenizer, sampler, max_input_tokens, max_output_tokens);
-}
-```
-
-Finally, download the following files into the same directory as main.cpp:
-
-```
-curl -O https://raw.githubusercontent.com/pytorch/executorch/main/examples/llm_manual/basic_sampler.h
-curl -O https://raw.githubusercontent.com/pytorch/executorch/main/examples/llm_manual/basic_tokenizer.h
-```
-
-To learn more, see the [Runtime APIs Tutorial](../extension-module.md).
-
-### Building and Running
-
-ExecuTorch uses the CMake build system. To compile and link against the ExecuTorch runtime,
-include the ExecuTorch project via `add_directory` and link against `executorch` and additional
-dependencies.
-
-Create a file named CMakeLists.txt with the following content:
-
-```
-# CMakeLists.txt
-
-cmake_minimum_required(VERSION 3.19)
-project(nanogpt_runner)
-
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# Set options for executorch build.
-option(EXECUTORCH_ENABLE_LOGGING "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_DATA_LOADER "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_MODULE "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_TENSOR "" ON)
-option(EXECUTORCH_BUILD_KERNELS_OPTIMIZED "" ON)
-
-# Include the executorch subdirectory.
-add_subdirectory(
-  ${CMAKE_CURRENT_SOURCE_DIR}/third-party/executorch
-  ${CMAKE_BINARY_DIR}/executorch
-)
-
-add_executable(nanogpt_runner main.cpp)
-target_link_libraries(
-  nanogpt_runner
-  PRIVATE executorch
-          extension_module_static # Provides the Module class
-          extension_tensor # Provides the TensorPtr class
-          optimized_native_cpu_ops_lib # Provides baseline cross-platform
-                                       # kernels
-)
-```
-
-At this point, the working directory should contain the following files:
-
-- CMakeLists.txt
-- main.cpp
-- basic_tokenizer.h
-- basic_sampler.h
-- export_nanogpt.py
-- model.py
-- vocab.json
-- nanogpt.pte
-
-If all of these are present, you can now build and run:
-```bash
-(mkdir cmake-out && cd cmake-out && cmake ..)
-cmake --build cmake-out -j10
-./cmake-out/nanogpt_runner
-```
-
-You should see the message:
-
-```
-Enter model prompt:
-```
-
-Type some seed text for the model and press enter. Here we use "Hello world!" as
-an example prompt:
-
-```
-Enter model prompt: Hello world!
-Hello world!
-
-I'm not sure if you've heard of the "Curse of the Dragon" or not, but it's a very popular game in
-```
-
-At this point, it is likely to run very slowly. This is because ExecuTorch hasn't been told to optimize for
-specific hardware (delegation), and because it is doing all of the calculations in 32-bit floating point (no quantization).
-
-## Delegation
-
-While ExecuTorch provides a portable, cross-platform implementation for all
-operators, it also provides specialized backends for a number of different
-targets. These include, but are not limited to, x86 and ARM CPU acceleration via
-the XNNPACK backend, Apple acceleration via the Core ML backend and Metal
-Performance Shader (MPS) backend, and GPU acceleration via the Vulkan backend.
-
-Because optimizations are specific to a given backend, each pte file is specific
-to the backend(s) targeted at export. To support multiple devices, such as
-XNNPACK acceleration for Android and Core ML for iOS, export a separate PTE file
-for each backend.
-
-To delegate a model to a specific backend during export, ExecuTorch uses the
-`to_edge_transform_and_lower()` function. This function takes the exported program
-from `torch.export` and a backend-specific partitioner object. The partitioner
-identifies parts of the computation graph that can be optimized by the target
-backend. Within `to_edge_transform_and_lower()`, the exported program is
-converted to an edge dialect program. The partitioner then delegates compatible
-graph sections to the backend for acceleration and optimization. Any graph parts
-not delegated are executed by ExecuTorch's default operator implementations.
-
-To delegate the exported model to a specific backend, we need to import its
-partitioner as well as edge compile config from ExecuTorch codebase first, then
-call `to_edge_transform_and_lower`.
-
-Here's an example of how to delegate nanoGPT to XNNPACK (if you're deploying to an Android phone for instance):
-
-```python
-# export_nanogpt.py
-
-# Load partitioner for Xnnpack backend
-from executorch.backends.xnnpack.partition.xnnpack_partitioner import XnnpackPartitioner
-
-# Model to be delegated to specific backend should use specific edge compile config
-from executorch.backends.xnnpack.utils.configs import get_xnnpack_edge_compile_config
-from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower
-
-import torch
-from torch.export import export
-from torch.nn.attention import sdpa_kernel, SDPBackend
-from torch.export import export_for_training
-
-from model import GPT
-
-# Load the nanoGPT model.
-model = GPT.from_pretrained('gpt2')
-
-# Create example inputs. This is used in the export process to provide
-# hints on the expected shape of the model input.
-example_inputs = (
-        torch.randint(0, 100, (1, model.config.block_size - 1), dtype=torch.long),
-    )
-
-# Set up dynamic shape configuration. This allows the sizes of the input tensors
-# to differ from the sizes of the tensors in `example_inputs` during runtime, as
-# long as they adhere to the rules specified in the dynamic shape configuration.
-# Here we set the range of 0th model input's 1st dimension as
-# [0, model.config.block_size].
-# See https://pytorch.org/executorch/main/concepts.html#dynamic-shapes
-# for details about creating dynamic shapes.
-dynamic_shape = (
-    {1: torch.export.Dim("token_dim", max=model.config.block_size - 1)},
-)
-
-# Trace the model, converting it to a portable intermediate representation.
-# The torch.no_grad() call tells PyTorch to exclude training-specific logic.
-with torch.nn.attention.sdpa_kernel([SDPBackend.MATH]), torch.no_grad():
-    m = export_for_training(model, example_inputs, dynamic_shapes=dynamic_shape).module()
-    traced_model = export(m, example_inputs, dynamic_shapes=dynamic_shape)
-
-# Convert the model into a runnable ExecuTorch program.
-# To be further lowered to Xnnpack backend, `traced_model` needs xnnpack-specific edge compile config
-edge_config = get_xnnpack_edge_compile_config()
-# Converted to edge program and then delegate exported model to Xnnpack backend
-# by invoking `to` function with Xnnpack partitioner.
-edge_manager = to_edge_transform_and_lower(traced_model, partitioner = [XnnpackPartitioner()], compile_config = edge_config)
-et_program = edge_manager.to_executorch()
-
-# Save the Xnnpack-delegated ExecuTorch program to a file.
-with open("nanogpt.pte", "wb") as file:
-    file.write(et_program.buffer)
-```
-
-Additionally, update CMakeLists.txt to build and link the XNNPACK backend to
-ExecuTorch runner.
-
-```
-cmake_minimum_required(VERSION 3.19)
-project(nanogpt_runner)
-
-set(CMAKE_CXX_STANDARD 17)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-# Set options for executorch build.
-option(EXECUTORCH_ENABLE_LOGGING "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_DATA_LOADER "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_MODULE "" ON)
-option(EXECUTORCH_BUILD_EXTENSION_TENSOR "" ON)
-option(EXECUTORCH_BUILD_KERNELS_OPTIMIZED "" ON)
-option(EXECUTORCH_BUILD_XNNPACK "" ON) # Build with Xnnpack backend
-
-# Include the executorch subdirectory.
-add_subdirectory(
-  ${CMAKE_CURRENT_SOURCE_DIR}/third-party/executorch
-  ${CMAKE_BINARY_DIR}/executorch
-)
-
-add_executable(nanogpt_runner main.cpp)
-target_link_libraries(
-  nanogpt_runner
-  PRIVATE executorch
-          extension_module_static # Provides the Module class
-          extension_tensor # Provides the TensorPtr class
-          optimized_native_cpu_ops_lib # Provides baseline cross-platform
-                                       # kernels
-          xnnpack_backend # Provides the XNNPACK CPU acceleration backend
-)
-```
-
-Keep the rest of the code the same. For more details refer to [Exporting
-to ExecuTorch](#step-1-exporting-to-executorch) and [Invoking the
-Runtime](#step-2-invoking-the-runtime) for more details
-
-At this point, the working directory should contain the following files:
-
-- CMakeLists.txt
-- main.cpp
-- basic_tokenizer.h
-- basic_sampler.h
-- export_nanogpt.py
-- model.py
-- vocab.json
-
-If all of these are present, you can now export Xnnpack delegated pte model:
-```bash
-python export_nanogpt.py
-```
-
-It will generate `nanogpt.pte`, under the same working directory.
-
-Then we can build and run the model by:
-```bash
-(rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake ..)
-cmake --build cmake-out -j10
-./cmake-out/nanogpt_runner
-```
-
-
-You should see the message:
-
-```
-Enter model prompt:
-```
-
-Type some seed text for the model and press enter. Here we use "Hello world!" as
-an example prompt:
-
-```
-Enter model prompt: Hello world!
-Hello world!
-
-I'm not sure if you've heard of the "Curse of the Dragon" or not, but it's a very popular game in
-```
-
-The delegated model should be noticeably faster compared to the non-delegated model.
-
-For more information regarding backend delegation, see the ExecuTorch guides
-for the [XNNPACK Backend](../backends-xnnpack.md),  [Core ML
-Backend](../backends-coreml.md) and [Qualcomm AI Engine Direct Backend](../backends-qualcomm.md).
-
-## Quantization
-
-Quantization refers to a set of techniques for running calculations and storing tensors using lower precision types.
-Compared to 32-bit floating point, using 8-bit integers can provide both a significant speedup and reduction in
-memory usage. There are many approaches to quantizing a model, varying in amount of pre-processing required, data
-types used, and impact on model accuracy and performance.
-
-Because compute and memory are highly constrained on mobile devices, some form of quantization is necessary to ship
-large models on consumer electronics. In particular, large language models, such as Llama2, may require quantizing
-model weights to 4 bits or less.
-
-Leveraging quantization requires transforming the model before export. PyTorch provides the pt2e (PyTorch 2 Export)
-API for this purpose. This example targets CPU acceleration using the XNNPACK delegate. As such, it needs to use the
- XNNPACK-specific quantizer. Targeting a different backend will require use of the corresponding quantizer.
-
-To use 8-bit integer dynamic quantization with the XNNPACK delegate, call `prepare_pt2e`, calibrate the model by
-running with a representative input, and then call `convert_pt2e`. This updates the computational graph to use
-quantized operators where available.
-
-```python
-# export_nanogpt.py
-
-from executorch.backends.transforms.duplicate_dynamic_quant_chain import (
-    DuplicateDynamicQuantChainPass,
-)
-from executorch.backends.xnnpack.quantizer.xnnpack_quantizer import (
-    get_symmetric_quantization_config,
-    XNNPACKQuantizer,
-)
-from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
-```
-
-```python
-# Use dynamic, per-channel quantization.
-xnnpack_quant_config = get_symmetric_quantization_config(
-    is_per_channel=True, is_dynamic=True
-)
-xnnpack_quantizer = XNNPACKQuantizer()
-xnnpack_quantizer.set_global(xnnpack_quant_config)
-
-m = export_for_training(model, example_inputs).module()
-
-# Annotate the model for quantization. This prepares the model for calibration.
-m = prepare_pt2e(m, xnnpack_quantizer)
-
-# Calibrate the model using representative inputs. This allows the quantization
-# logic to determine the expected range of values in each tensor.
-m(*example_inputs)
-
-# Perform the actual quantization.
-m = convert_pt2e(m, fold_quantize=False)
-DuplicateDynamicQuantChainPass()(m)
-
-traced_model = export(m, example_inputs)
-```
-
-Additionally, add or update the `to_edge_transform_and_lower()` call to use `XnnpackPartitioner`. This
-instructs ExecuTorch to optimize the model for CPU execution via the XNNPACK backend.
-
-```python
-from executorch.backends.xnnpack.partition.xnnpack_partitioner import (
-    XnnpackPartitioner,
-)
-```
-
-```python
-edge_config = get_xnnpack_edge_compile_config()
-# Convert to edge dialect and lower to XNNPack.
-edge_manager = to_edge_transform_and_lower(traced_model, partitioner = [XnnpackPartitioner()], compile_config = edge_config)
-et_program = edge_manager.to_executorch()
-
-with open("nanogpt.pte", "wb") as file:
-    file.write(et_program.buffer)
-```
-
-Then run:
-```bash
-python export_nanogpt.py
-./cmake-out/nanogpt_runner
-```
-
-For more information, see [Quantization in ExecuTorch](../quantization-overview.md).
-
-## Profiling and Debugging
-After lowering a model by calling `to_edge_transform_and_lower()`, you may want to see what got delegated and what didn’t. ExecuTorch
-provides utility methods to give insight on the delegation. You can use this information to gain visibility into
-the underlying computation and diagnose potential performance issues. Model authors can use this information to
-structure the model in a way that is compatible with the target backend.
-
-### Visualizing the Delegation
-
-The `get_delegation_info()` method provides a summary of what happened to the model after the `to_edge_transform_and_lower()` call:
-
-```python
-from executorch.devtools.backend_debug import get_delegation_info
-from tabulate import tabulate
-
-# ... After call to to_edge_transform_and_lower(), but before to_executorch()
-graph_module = edge_manager.exported_program().graph_module
-delegation_info = get_delegation_info(graph_module)
-print(delegation_info.get_summary())
-df = delegation_info.get_operator_delegation_dataframe()
-print(tabulate(df, headers="keys", tablefmt="fancy_grid"))
-```
-
-For nanoGPT targeting the XNNPACK backend, you might see the following (note that the numbers below are for illustration purposes only and actual values may vary):
-```
-Total  delegated  subgraphs:  145
-Number  of  delegated  nodes:  350
-Number  of  non-delegated  nodes:  760
-```
-
-
-|    |  op_type                                 |  # in_delegated_graphs  |  # in_non_delegated_graphs  |
-|----|---------------------------------|------- |-----|
-|  0  |  aten__softmax_default  |  12  |  0  |
-|  1  |  aten_add_tensor  |  37  |  0  |
-|  2  |  aten_addmm_default  |  48  |  0  |
-|  3  |  aten_any_dim  |  0  |  12  |
-|      |  ...  |    |    |
-|  25  |  aten_view_copy_default  |  96  |  122  |
-|      |  ...  |    |    |
-|  30  |  Total  |  350  |  760  |
-
-From the table, the operator `aten_view_copy_default` appears 96 times in delegate graphs and 122 times in non-delegated graphs.
-To see a more detailed view, use the `format_delegated_graph()` method to get a formatted str of printout of the whole graph or use `print_delegated_graph()` to print directly:
-
-```python
-from executorch.exir.backend.utils import format_delegated_graph
-graph_module = edge_manager.exported_program().graph_module
-print(format_delegated_graph(graph_module))
-```
-This may generate a large amount of output for large models. Consider using "Control+F" or "Command+F" to locate the operator you’re interested in
-(e.g. “aten_view_copy_default”). Observe which instances are not under lowered graphs.
-
-In the fragment of the output for nanoGPT below, observe that a transformer module has been delegated to XNNPACK while the where operator is not.
-
-```
-%aten_where_self_22 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.where.self](args = (%aten_logical_not_default_33, %scalar_tensor_23, %scalar_tensor_22), kwargs = {})
-%lowered_module_144 : [num_users=1] = get_attr[target=lowered_module_144]
-backend_id: XnnpackBackend
-lowered graph():
-    %p_transformer_h_0_attn_c_attn_weight : [num_users=1] = placeholder[target=p_transformer_h_0_attn_c_attn_weight]
-    %p_transformer_h_0_attn_c_attn_bias : [num_users=1] = placeholder[target=p_transformer_h_0_attn_c_attn_bias]
-    %getitem : [num_users=1] = placeholder[target=getitem]
-    %sym_size : [num_users=2] = placeholder[target=sym_size]
-    %aten_view_copy_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.view_copy.default](args = (%getitem, [%sym_size, 768]), kwargs = {})
-    %aten_permute_copy_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.permute_copy.default](args = (%p_transformer_h_0_attn_c_attn_weight, [1, 0]), kwargs = {})
-    %aten_addmm_default : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.addmm.default](args = (%p_transformer_h_0_attn_c_attn_bias, %aten_view_copy_default, %aten_permute_copy_default), kwargs = {})
-    %aten_view_copy_default_1 : [num_users=1] = call_function[target=executorch.exir.dialects.edge._ops.aten.view_copy.default](args = (%aten_addmm_default, [1, %sym_size, 2304]), kwargs = {})
-    return [aten_view_copy_default_1]
-```
-
-### Performance Analysis
-
-Through the ExecuTorch Developer Tools, users are able to profile model execution, giving timing information for each operator in the model.
-
-#### Prerequisites
-
-##### ETRecord generation (Optional)
-
-An ETRecord is an artifact generated at the time of export that contains model graphs and source-level metadata linking the ExecuTorch program to the original PyTorch model. You can view all profiling events without an ETRecord, though with an ETRecord, you will also be able to link each event to the types of operators being executed, module hierarchy, and stack traces of the original PyTorch source code. For more information, see [the ETRecord docs](../etrecord.rst).
-
-
-In your export script, after calling `to_edge()` and `to_executorch()`, call `generate_etrecord()` with the `EdgeProgramManager` from `to_edge()` and the `ExecuTorchProgramManager` from `to_executorch()`. Make sure to copy the `EdgeProgramManager`, as the call to `to_edge_transform_and_lower()` mutates the graph in-place.
-
-```
-# export_nanogpt.py
-
-import copy
-from executorch.devtools import generate_etrecord
-
-# Make the deep copy immediately after to to_edge()
-edge_manager_copy = copy.deepcopy(edge_manager)
-
-# ...
-# Generate ETRecord right after to_executorch()
-etrecord_path = "etrecord.bin"
-generate_etrecord(etrecord_path, edge_manager_copy, et_program)
-```
-
-Run the export script and the ETRecord will be generated as `etrecord.bin`.
-
-##### ETDump generation
-
-An ETDump is an artifact generated at runtime containing a trace of the model execution. For more information, see [the ETDump docs](../etdump.md).
-
-Include the ETDump header and namespace in your code.
-```cpp
-// main.cpp
-
-#include <executorch/devtools/etdump/etdump_flatcc.h>
-
-using executorch::etdump::ETDumpGen;
-using torch::executor::etdump_result;
-```
-
-Create an Instance of the ETDumpGen class and pass it to the Module constructor.
-```cpp
-std::unique_ptr<ETDumpGen> etdump_gen_ = std::make_unique<ETDumpGen>();
-Module model("nanogpt.pte", Module::LoadMode::MmapUseMlockIgnoreErrors, std::move(etdump_gen_));
-```
-
-After calling `generate()`, save the ETDump to a file. You can capture multiple
-model runs in a single trace, if desired.
-```cpp
-ETDumpGen* etdump_gen = static_cast<ETDumpGen*>(model.event_tracer());
-
-ET_LOG(Info, "ETDump size: %zu blocks", etdump_gen->get_num_blocks());
-etdump_result result = etdump_gen->get_etdump_data();
-if (result.buf != nullptr && result.size > 0) {
-    // On a device with a file system, users can just write it to a file.
-    FILE* f = fopen("etdump.etdp", "w+");
-    fwrite((uint8_t*)result.buf, 1, result.size, f);
-    fclose(f);
-    free(result.buf);
-}
-```
-
-Additionally, update CMakeLists.txt to build with Developer Tools and enable events to be traced and logged into ETDump:
-
-```
-option(EXECUTORCH_ENABLE_EVENT_TRACER "" ON)
-option(EXECUTORCH_BUILD_DEVTOOLS "" ON)
-
-# ...
-
-target_link_libraries(
-    # ... omit existing ones
-    etdump) # Provides event tracing and logging
-
-target_compile_options(executorch PUBLIC -DET_EVENT_TRACER_ENABLED)
-target_compile_options(portable_ops_lib PUBLIC -DET_EVENT_TRACER_ENABLED)
-```
-Build and run the runner, you will see a file named “etdump.etdp” is generated. (Note that this time we build in release mode to get around a flatccrt build limitation.)
-```bash
-(rm -rf cmake-out && mkdir cmake-out && cd cmake-out && cmake -DCMAKE_BUILD_TYPE=Release ..)
-cmake --build cmake-out -j10
-./cmake-out/nanogpt_runner
-```
-
-#### Analyze with Inspector APIs
-
-Once you’ve collected debug artifacts ETDump (and optionally an ETRecord), you can use the Inspector API to view performance information.
-
-```python
-from executorch.devtools import Inspector
-
-inspector = Inspector(etdump_path="etdump.etdp")
-# If you also generated an ETRecord, then pass that in as well: `inspector = Inspector(etdump_path="etdump.etdp", etrecord="etrecord.bin")`
-
-with open("inspector_out.txt", "w") as file:
-    inspector.print_data_tabular(file)
-```
-This prints the performance data in a tabular format in “inspector_out.txt”, with each row being a profiling event. Top rows look like this:
-![](../_static/img/llm_manual_print_data_tabular.png)
-<a href="../_static/img/llm_manual_print_data_tabular.png" target="_blank">View in full size</a>
-
-To learn more about the Inspector and the rich functionality it provides, see the [Inspector API Reference](../model-inspector.rst).
-
-## Custom Kernels
-With the ExecuTorch custom operator APIs, custom operator and kernel authors can easily bring in their kernel into PyTorch/ExecuTorch.
-
-There are three steps to use custom kernels in ExecuTorch:
-
-1.  [Write the custom kernel](../kernel-library-custom-aten-kernel.md#c-api-for-custom-ops) using ExecuTorch types.
-2.  [Compile and link the custom kernel](../kernel-library-custom-aten-kernel.md#compile-and-link-the-custom-kernel) to both AOT Python environment as well as the runtime binary.
-3.  [Source-to-source transformation](../kernel-library-custom-aten-kernel.md#using-a-custom-operator-in-a-model) to swap an operator with a custom op.
+To follow this guide, you'll need to install ExecuTorch. Please see [Setting Up ExecuTorch](../getting-started.md#installation).
 
-For more information, see [PyTorch Custom Operators](https://pytorch.org/tutorials/advanced/torch_script_custom_ops.html) and
-and [ExecuTorch Kernel Registration](../kernel-library-custom-aten-kernel.md).
+## Next steps
 
-## How to Build Mobile Apps
-See the instructions for building and running LLMs using ExecuTorch on iOS and Android.
+Deploying LLMs to ExecuTorch can be boiled down to a two-step process: (1) exporting the LLM to a `.pte` file and (2) running the `.pte` file using our C++ APIs or Swift/Java bindings.
 
-* **[iOS ExecuTorch LLaMA Demo App](llama-demo-ios.md)**
-* **[Android ExecuTorch LLaMA Demo App](llama-demo-android.md)**
+- [Exporting LLMs](export-llm.md)
+- [Exporting custom LLMs](export-custom-llm.md)
+- [Running with C++](run-with-c-plus-plus.md)
+- [Running on Android (XNNPack)](llama-demo-android.md)
+- [Running on Android (Qualcomm)](build-run-llama3-qualcomm-ai-engine-direct-backend.md)
+- [Running on iOS](llama-demo-ios.md)
diff --git a/docs/source/llm/run-on-ios.md b/docs/source/llm/run-on-ios.md
new file mode 100644
index 00000000000..3348a03feb3
--- /dev/null
+++ b/docs/source/llm/run-on-ios.md
@@ -0,0 +1,126 @@
+# Running LLMs on iOS
+
+ExecuTorch’s LLM-specific runtime components provide an experimental Objective-C and Swift components around the core C++ LLM runtime.
+
+## Prerequisites
+
+Make sure you have a model and tokenizer files ready, as described in the prerequisites section of the [Running LLMs with C++](run-with-c-plus-plus.md) guide.
+
+## Runtime API
+
+Once linked against the [`executorch_llm`](../using-executorch-ios.md) framework, you can import the necessary components.
+
+### Importing
+
+Objective-C:
+```objectivec
+#import <ExecuTorchLLM/ExecuTorchLLM.h>
+```
+
+Swift:
+```swift
+import ExecuTorchLLM
+```
+
+### TextLLMRunner
+
+The `ExecuTorchTextLLMRunner` class (bridged to Swift as `TextLLMRunner`) provides a simple Objective-C/Swift interface for loading a text-generation model, configuring its tokenizer with custom special tokens, generating token streams, and stopping execution.
+This API is experimental and subject to change.
+
+#### Initialization
+
+Create a runner by specifying paths to your serialized model (`.pte`) and tokenizer data, plus an array of special tokens to use during tokenization.
+Initialization itself is lightweight and doesn’t load the program data immediately.
+
+Objective-C:
+```objectivec
+NSString *modelPath     = [[NSBundle mainBundle] pathForResource:@"llama-3.2-instruct" ofType:@"pte"];
+NSString *tokenizerPath = [[NSBundle mainBundle] pathForResource:@"tokenizer" ofType:@"model"];
+NSArray<NSString *> *specialTokens = @[ @"<|bos|>", @"<|eos|>" ];
+
+ExecuTorchTextLLMRunner *runner = [[ExecuTorchTextLLMRunner alloc] initWithModelPath:modelPath
+                                                                       tokenizerPath:tokenizerPath
+                                                                       specialTokens:specialTokens];
+```
+
+Swift:
+```swift
+let modelPath     = Bundle.main.path(forResource: "llama-3.2-instruct", ofType: "pte")!
+let tokenizerPath = Bundle.main.path(forResource: "tokenizer", ofType: "model")!
+let specialTokens = ["<|bos|>", "<|eos|>"]
+
+let runner = TextLLMRunner(
+  modelPath: modelPath,
+  tokenizerPath: tokenizerPath,
+  specialTokens: specialTokens
+)
+```
+
+#### Loading
+
+Explicitly load the model before generation to avoid paying the load cost during your first `generate` call.
+
+Objective-C:
+```objectivec
+NSError *error = nil;
+BOOL success = [runner loadWithError:&error];
+if (!success) {
+  NSLog(@"Failed to load: %@", error);
+}
+```
+
+Swift:
+```swift
+do {
+  try runner.load()
+} catch {
+  print("Failed to load: \(error)")
+}
+```
+
+#### Generating
+
+Generate up to a given number of tokens from an initial prompt. The callback block is invoked once per token as it’s produced.
+
+Objective-C:
+```objectivec
+NSError *error = nil;
+BOOL success = [runner generate:@"Once upon a time"
+                 sequenceLength:50
+              withTokenCallback:^(NSString *token) {
+                NSLog(@"Generated token: %@", token);
+              }
+                          error:&error];
+if (!success) {
+  NSLog(@"Generation failed: %@", error);
+}
+```
+
+Swift:
+```swift
+do {
+  try runner.generate("Once upon a time", sequenceLength: 50) { token in
+    print("Generated token:", token)
+  }
+} catch {
+  print("Generation failed:", error)
+}
+```
+
+#### Stopping Generation
+
+If you need to interrupt a long‐running generation, call:
+
+Objective-C:
+```objectivec
+[runner stop];
+```
+
+Swift:
+```swift
+runner.stop()
+```
+
+## Demo
+
+Get hands-on with our [LLaMA iOS Demo App](llama-demo-ios.md) to see the LLM runtime APIs in action.
diff --git a/docs/source/llm/run-with-c-plus-plus.md b/docs/source/llm/run-with-c-plus-plus.md
new file mode 100644
index 00000000000..77c8990b42d
--- /dev/null
+++ b/docs/source/llm/run-with-c-plus-plus.md
@@ -0,0 +1,296 @@
+# Running LLMs with C++
+
+This guide explains how to use ExecuTorch's C++ runner library to run LLM models that have been exported to the `.pte` format. The runner library provides a high-level API for text generation with LLMs, handling tokenization, inference, and token generation.
+
+## Prerequisites
+
+Before you begin, make sure you have:
+
+1. A model exported to `.pte` format using the `export_llm` API as described in [Exporting popular LLMs out of the box](export-llm.md) or [Exporting custom LLMs](export-custom-llm.md).
+   - Please also see [Model Metadata](#model-metadata) section for important metadata to be serialized into `.pte`.
+2. A tokenizer file compatible with your model
+   - For HuggingFace tokenizers, this is a JSON file `tokenizer.json`
+   - For SentencePiece tokenizers, this is is a `tokenizer.model` file and normally live alongside the weights file
+3. CMake and a C++ compiler installed
+   - CMake version 3.29 or higher
+   - g++ or clang compiler
+
+## Model Metadata
+
+The metadata includes several important configuration parameters to be included during export step, which will be used by the runner library:
+
+1. **`enable_dynamic_shape`**: Whether the model supports dynamic input shapes
+2. **`max_seq_len`**: Maximum sequence length the model can handle
+3. **`max_context_len`**: Maximum context length for KV cache
+4. **`use_kv_cache`**: Whether the model uses KV cache for efficient generation
+6. **`get_bos_id`**: Beginning-of-sequence token ID
+7. **`get_eos_ids`**: End-of-sequence token IDs
+
+### Adding Metadata During Export
+
+To ensure your model has the necessary metadata, you can specify it during export using the `metadata` parameter in the export configuration:
+
+```python
+# export_llm
+python -m extension.llm.export.export_llm \
+  --config path/to/config.yaml \
+  +base.metadata='{"get_bos_id":128000, "get_eos_ids":[128009, 128001], "get_max_context_len":4096}'
+```
+## Building the Runner Library
+
+The ExecuTorch LLM runner library can be built using CMake. To integrate it into your project:
+
+1. Add ExecuTorch as a dependency in your CMake project
+2. Enable the required components (extension_module, extension_tensor, etc.)
+3. Link your application against the `extension_llm_runner` library
+
+Here's a simplified example of the CMake configuration:
+
+```cmake
+# Enable required components
+set_overridable_option(EXECUTORCH_BUILD_EXTENSION_MODULE ON)
+set_overridable_option(EXECUTORCH_BUILD_EXTENSION_TENSOR ON)
+set_overridable_option(EXECUTORCH_BUILD_EXTENSION_LLM_RUNNER ON)
+
+# Add ExecuTorch as a dependency
+add_subdirectory(executorch)
+
+# Link against the LLM runner library
+target_link_libraries(your_app PRIVATE extension_llm_runner)
+```
+
+## Building the Llama Runner
+
+ExecuTorch provides a complete example of a C++ runner for Llama models in the [`examples/models/llama`](https://github.com/pytorch/executorch/blob/main/examples/models/llama/README.md#step-3-run-on-your-computer-to-validate) directory. This runner demonstrates how to use the LLM runner library to run Llama models exported to the `.pte` format.
+
+Please note that this runner library is not limited to Llama models and can be used with any text-only decoder-only LLM model that has been exported to the `.pte`.
+
+## Basic Usage Example
+
+Here's a simplified example of using the runner:
+
+```cpp
+#include <executorch/extension/llm/runner/text_llm_runner.h>
+
+using namespace executorch::extension::llm;
+
+int main() {
+  // Load tokenizer and create runner
+  auto tokenizer = load_tokenizer("path/to/tokenizer.json", nullptr, std::nullopt, 0, 0);
+  auto runner = create_text_llm_runner("path/to/model.pte", std::move(tokenizer));
+
+  // Load the model
+  runner->load();
+
+  // Configure generation
+  GenerationConfig config;
+  config.max_new_tokens = 100;
+  config.temperature = 0.8f;
+
+  // Generate text with streaming output
+  runner->generate("Hello, world!", config,
+    [](const std::string& token) { std::cout << token << std::flush; },
+    nullptr);
+
+  return 0;
+}
+```
+
+## The Runner API Architecture
+
+The ExecuTorch LLM runner library is designed with a modular architecture that separates concerns between different components of the text generation pipeline.
+
+### IRunner Interface
+
+The `IRunner` interface (`irunner.h`) defines the core functionality for LLM text generation. This interface serves as the primary abstraction for interacting with LLM models:
+
+```cpp
+class IRunner {
+public:
+  virtual ~IRunner() = default;
+  virtual bool is_loaded() const = 0;
+  virtual runtime::Error load() = 0;
+  virtual runtime::Error generate(...) = 0;
+  virtual runtime::Error generate_from_pos(...) = 0;
+  virtual void stop() = 0;
+};
+```
+
+Let's examine each method in detail:
+
+```c++
+bool is_loaded() const
+```
+
+Checks if the model and all necessary resources have been loaded into memory and are ready for inference. This method is useful for verifying the runner's state before attempting to generate text.
+
+```c++
+runtime::Error load()
+```
+
+Loads the model and prepares it for inference. This includes:
+- Loading the model weights from the `.pte` file
+- Initializing any necessary buffers or caches
+- Preparing the execution environment
+
+This method should be called before any generation attempts. It returns an `Error` object indicating success or failure.
+
+```c++
+runtime::Error generate(
+   const std::string& prompt,
+   const GenerationConfig& config,
+   std::function<void(const std::string&)> token_callback,
+   std::function<void(const Stats&)> stats_callback)
+```
+The primary method for text generation. It takes:
+
+- `prompt`: The input text to generate from
+- `config`: Configuration parameters controlling the generation process
+- `token_callback`: A callback function that receives each generated token as a string
+- `stats_callback`: A callback function that receives performance statistics after generation completes
+
+The token callback is called for each token as it's generated, allowing for streaming output. The stats callback provides detailed performance metrics after generation completes.
+
+```c++
+runtime::Error generate_from_pos(
+   const std::string& prompt,
+   int64_t start_pos,
+   const GenerationConfig& config,
+   std::function<void(const std::string&)> token_callback,
+   std::function<void(const Stats&)> stats_callback)
+```
+
+An advanced version of `generate()` that allows starting generation from a specific position in the KV cache. This is useful for continuing generation from a previous state.
+
+```c++
+void stop()
+```
+
+Immediately stops the generation loop. This is typically called from another thread to interrupt a long-running generation.
+
+### GenerationConfig Structure
+
+The `GenerationConfig` struct controls various aspects of the generation process:
+
+```cpp
+struct GenerationConfig {
+  bool echo = true;                // Whether to echo the input prompt in the output
+  int32_t max_new_tokens = -1;     // Maximum number of new tokens to generate
+  bool warming = false;            // Whether this is a warmup run
+  int32_t seq_len = -1;            // Maximum number of total tokens
+  float temperature = 0.8f;        // Temperature for sampling
+  int32_t num_bos = 0;             // Number of BOS tokens to add
+  int32_t num_eos = 0;             // Number of EOS tokens to add
+
+  // Helper method to resolve the actual max_new_tokens based on constraints
+  int32_t resolve_max_new_tokens(int32_t max_context_len, int32_t num_prompt_tokens) const;
+};
+```
+
+The `resolve_max_new_tokens` method handles the logic of determining how many tokens can be generated based on:
+- The model's maximum context length
+- The number of tokens in the prompt
+- The user-specified maximum sequence length and maximum new tokens
+
+### Implementation Components
+
+The runner library consists of several specialized components that work together:
+
+#### TextLLMRunner
+
+The main implementation of the `IRunner` interface that orchestrates the text generation process. It manages:
+
+1. Tokenization of input text
+2. Prefilling the KV cache with prompt tokens
+3. Generating new tokens one by one
+4. Collecting performance statistics
+
+#### TextPrefiller
+
+Responsible for processing the initial prompt tokens and filling the KV cache. Key features:
+
+- Efficiently processes large prompts
+- Handles dynamic sequence lengths
+- Supports parallel prefilling for performance optimization
+
+#### TextTokenGenerator
+
+Generates new tokens one by one in an autoregressive manner. It:
+
+- Manages the token generation loop
+- Applies temperature-based sampling
+- Detects end-of-sequence conditions
+- Streams tokens as they're generated
+
+#### TextDecoderRunner
+
+Interfaces with the ExecuTorch Module to run the model forward pass. It:
+
+- Manages inputs and outputs to the model
+- Handles KV cache updates
+- Converts logits to tokens via sampling
+
+## Tokenizer Support
+
+The runner library supports multiple tokenizer formats through a unified interface:
+
+```cpp
+std::unique_ptr<tokenizers::Tokenizer> tokenizer = load_tokenizer(
+    tokenizer_path,  // Path to tokenizer file
+    nullptr,         // Optional special tokens
+    std::nullopt,    // Optional regex pattern (for TikToken)
+    0,               // BOS token index
+    0                // EOS token index
+);
+```
+
+Supported tokenizer formats include:
+
+1. **HuggingFace Tokenizers**: JSON format tokenizers
+2. **SentencePiece**: `.model` format tokenizers
+3. **TikToken**: BPE tokenizers
+4. **Llama2c**: BPE tokenizers in the Llama2.c format
+
+For custom tokenizers, you can find implementations in the [pytorch-labs/tokenizers](https://github.com/pytorch-labs/tokenizers) repository.
+
+
+## Other APIs
+
+### Model Warmup
+
+For more accurate timing and optimal performance, you should perform a warmup run before actual inference:
+
+```cpp
+runner->warmup("Hello world", 10);  // Generate 10 tokens as warmup
+```
+
+During warmup:
+
+1. A special `GenerationConfig` is created with:
+   - `echo = false`: The prompt is not included in the output
+   - `warming = true`: Indicates this is a warmup run
+   - `max_new_tokens`: Set to the specified number of tokens to generate
+
+2. The model runs through the entire generation pipeline:
+   - Loading the model (if not already loaded)
+   - Tokenizing the prompt
+   - Prefilling the KV cache
+   - Generating the specified number of tokens
+
+3. Special behavior during warmup:
+   - Tokens are not displayed to the console
+   - The runner logs "Doing a warmup run..." and "Warmup run finished!" messages
+
+4. After warmup:
+   - The `Stats` object is reset to clear performance metrics
+   - The model remains loaded and ready for actual inference
+
+Warmup is particularly important for accurate benchmarking as the first inference often includes one-time initialization costs that would skew performance measurements.
+
+### Memory Usage Monitoring
+
+You can monitor memory usage with the `Stats` object:
+
+```cpp
+std::cout << "RSS after loading: " << get_rss_bytes() / 1024.0 / 1024.0 << " MiB" << std::endl;
+```
diff --git a/docs/source/model-inspector.rst b/docs/source/model-inspector.rst
index d80a8960b1b..4cda6580189 100644
--- a/docs/source/model-inspector.rst
+++ b/docs/source/model-inspector.rst
@@ -106,6 +106,21 @@ get_exported_program
     Equality constraints: []
 
 
+calculate_numeric_gap
+~~~~~~~~~~~~~~~~~~
+
+.. autofunction:: executorch.devtools.Inspector.calculate_numeric_gap
+
+.. _example-usage-4:
+
+**Example Usage:**
+
+.. code:: python
+
+    print(inspector.calculate_numeric_gap("L1"))
+
+.. image:: _static/img/calculate_numeric_gap.png
+
 Inspector Attributes
 --------------------
 
diff --git a/docs/source/ptd-file-format.md b/docs/source/ptd-file-format.md
new file mode 100644
index 00000000000..6381e8a071c
--- /dev/null
+++ b/docs/source/ptd-file-format.md
@@ -0,0 +1,144 @@
+# `.ptd` file format
+
+ExecuTorch `.ptd` files are serialized as modified binary flatbuffer
+files with data segments appended. They provide a way to store named data using
+the FlatTensor format. Named data can be tensors or opaque blob data (usually for backends that do not expose data format).
+
+Code related to the PTD file format is in the `//executorch/extension/flat_tensor/` directory.
+
+```
+             ┌───────────────────────────────────┐
+             │Standard flatbuffer header         │
+             ├───────────────────────────────────┤
+             │ExecuTorch extended header         │
+             ├───────────────────────────────────┤
+             │Flatbuffer-serialized metadata     │
+             │(FlatTensor)                       │
+             │                                   │
+          ┌─ ├───────────────────────────────────┤
+          │  │Padding                            │
+          │  ├───────────────────────────────────┤
+          │  │Data segment                       │
+          │  │                                   │
+          │  │                                   │
+          │  ├───────────────────────────────────┤
+          │  │Padding                            │
+   Blobs ─┤  ├───────────────────────────────────┤
+          │  │Data segment                       │
+          │  │                                   │
+          │  │                                   │
+          │  ├───────────────────────────────────┤
+          │  │Padding                            │
+          │  ├───────────────────────────────────┤
+          │  │...                                │
+          └─ └───────────────────────────────────┘
+```
+
+## Compatibility
+
+PTD files are designed for storing named data that can be loaded by ExecuTorch
+models.
+
+## Headers
+
+PTD files can be recognized by the magic string at byte offset 4, beginning with `FT`
+and followed by two ASCII decimal digits (file identifier from the FlatBuffers schema).
+
+PTD files have an extended header at byte offset 8, recognized by the magic string
+`FH01`. This header includes the size and offset information for both the
+flatbuffer-serialized metadata and the data segments that follow.
+
+Note that this header is ExecuTorch-specific, but even when present it does not
+upset most flatbuffer-parsing code (apart from the rarely-used
+`GetBufferStartFromRootPointer()`).
+
+All numbers are little-endian, regardless of the host system.
+
+Header layout:
+```
+[0..3] uint32_t byte offset to the beginning of the flatbuffer root table.
+[4..7] File magic bytes: "FT" followed by two ASCII decimal digits. The digits
+       correspond to the FlatBuffers file identifier.
+Extended header (always present):
+|  [8..11] Extended header magic bytes: "FH01" - FlatTensor Header version 01.
+| [12..15] uint32_t size of this extended header in bytes, including the magic
+|          header and this size field. Currently fixed at 40 bytes.
+| [16..23] uint64_t offset (from byte offset zero) to the start of the
+|          flatbuffer data.
+| [24..31] uint64_t size of the flatbuffer-encoded tensor metadata in bytes.
+| [32..39] uint64_t offset (from byte offset zero) to the start of the first
+|          data segment.
+| [40..47] uint64_t total size of all data segments in bytes.
+End of extended header.
+```
+
+Example:
+```
+        Offset to flatbuffer root (0x44)
+        |            File magic ("FT01")
+        |            |            Extended header magic ("FH01")
+        |            |            |            Extended header size (0x28)
+        vvvvvvvvvvv  vvvvvvvvvvv  vvvvvvvvvvv  vvvvvvvvvvv
+0x0000  44 00 00 00  46 54 30 31  46 48 30 31  28 00 00 00
+0x0010  30 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00
+0x0020  30 01 00 00  00 00 00 00  20 00 00 00  00 00 00 00
+        ^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^
+        |                         | Flatbuffer size (0x100)
+        |                         | Segment data size (0x20)
+        Segment base offset (0x130)
+```
+Note: this example comes from inspecting the ModuleAddMul.ptd file.
+```
+python -m test.models.export_program --modules "ModuleAddMul" --external-constants --outdir .
+
+xxd -l 64 ModuleAddMulProgram.ptd
+```
+
+## FlatTensor
+
+See `//executorch/extension/flat_tensor/serialize/flat_tensor.fbs` for the
+FlatTensor flatbuffer schema.
+
+The flatbuffer-encoded metadata follows the headers and contains:
+
+- **Schema version**: Version information for compatibility.
+- **Data segments**: List of segment descriptors with offset and size information.
+- **Named data**: List of named data entries, each containing:
+  - **Key**: String identifier for the data blob.
+  - **Segment index**: Reference to the data segment containing the blob.
+  - **Tensor layout**: Optional metadata including scalar type, sizes and dim order, if the data segment contains a tensor.
+
+### Tensor Layout
+
+If a data segment contains a canonical tensor, it may have associated layout information:
+- **Scalar type**: Data type (float32, int32, etc.) using ExecutorTorch scalar types.
+- **Sizes**: Dimensions of the tensor.
+- **Dim order**: Memory layout order specifying how dimensions are arranged in memory.
+
+## Data segments
+
+The `FlatTensor.segments` list in the metadata contains offset and size
+information about each data segment. Offsets in this list are relative to
+the segment base offset specified in the extended header.
+
+Each segment contains:
+- **Offset**: Relative offset from the segment base offset.
+- **Size**: Size of the valid data in bytes (may be followed by padding).
+
+## Named data access
+
+Tensors are accessed by string keys through the `named_data` list. Each entry
+maps a string key to:
+1. A segment index pointing to the raw data.
+2. Optional tensor layout metadata, if the data segment contains a tensor.
+
+This design allows:
+- Multiple named data blobs to reference the same data segment.
+- Access to tensor layout data without loading the entire blob.
+
+## Usage
+
+PTD files are used to store data outside of the PTE file. Some use-cases:
+- On-device training: checkpointing for model weights.
+- Deduplication: sharing model weights between multiple executable PTE files.
+- Flexible deployment: allow async updates between program and data.
diff --git a/docs/source/quantization-overview.md b/docs/source/quantization-overview.md
index cfc04e6dc59..eff7812134b 100644
--- a/docs/source/quantization-overview.md
+++ b/docs/source/quantization-overview.md
@@ -1,38 +1,72 @@
 # Quantization Overview
-Quantization is a process that reduces the precision of computations and lowers memory footprint in the model. To learn more, please visit the [ExecuTorch concepts page](concepts.md#quantization). This is particularly useful for edge devices including wearables, embedded devices and microcontrollers, which typically have limited resources such as processing power, memory, and battery life. By using quantization, we can make our models more efficient and enable them to run effectively on these devices.
 
-In terms of flow, quantization happens early in the ExecuTorch stack:
+Quantization is a technique that reduces the precision of numbers used in a model’s computations and stored weights—typically from 32-bit floats to 8-bit integers. This reduces the model’s memory footprint, speeds up inference, and lowers power consumption, often with minimal loss in accuracy.
 
-![ExecuTorch Entry Points](_static/img/executorch-entry-points.png)
+Quantization is especially important for deploying models on edge devices such as wearables, embedded systems, and microcontrollers, which often have limited compute, memory, and battery capacity. By quantizing models, we can make them significantly more efficient and suitable for these resource-constrained environments.
 
-A more detailed workflow can be found in the [ExecuTorch tutorial](https://pytorch.org/executorch/main/tutorials/export-to-executorch-tutorial).
 
-Quantization is usually tied to execution backends that have quantized operators implemented. Thus each backend is opinionated about how the model should be quantized, expressed in a backend specific ``Quantizer`` class. ``Quantizer`` provides API for modeling users in terms of how they want their model to be quantized and also passes on the user intention to quantization workflow.
+# Quantization in ExecuTorch
+ExecuTorch uses [torchao](https://github.com/pytorch/ao/tree/main/torchao) as its quantization library. This integration allows ExecuTorch to leverage PyTorch-native tools for preparing, calibrating, and converting quantized models.
 
-Backend developers will need to implement their own ``Quantizer`` to express how different operators or operator patterns are quantized in their backend. This is accomplished via [Annotation API](https://pytorch.org/tutorials/prototype/pt2e_quantizer.html) provided by quantization workflow. Since ``Quantizer`` is also user facing, it will expose specific APIs for modeling users to configure how they want the model to be quantized. Each backend should provide their own API documentation for their ``Quantizer``.
 
-Modeling users will use the ``Quantizer`` specific to their target backend to quantize their model, e.g. ``XNNPACKQuantizer``.
+Quantization in ExecuTorch is backend-specific. Each backend defines how models should be quantized based on its hardware capabilities. Most ExecuTorch backends use the torchao [PT2E quantization](https://docs.pytorch.org/ao/main/tutorials_source/pt2e_quant_ptq.html) flow, which works on models exported with torch.export and enables quantization that is tailored for each backend.
 
-For an example quantization flow with ``XNNPACKQuantizer``, more documentation and tutorials, please see ``Performing Quantization`` section in [ExecuTorch tutorial](https://pytorch.org/executorch/main/tutorials/export-to-executorch-tutorial).
+The PT2E quantization workflow has three main steps:
 
-## Source Quantization: Int8DynActInt4WeightQuantizer
+1. Configure a backend-specific quantizer.
+2. Prepare, calibrate, convert, and evalute the quantized model in PyTorch
+3. Lower the model to the target backend
 
-In addition to export based quantization (described above), ExecuTorch wants to highlight source based quantizations, accomplished via [torchao](https://github.com/pytorch/ao). Unlike export based quantization, source based quantization directly modifies the model prior to export. One specific example is `Int8DynActInt4WeightQuantizer`.
+## 1. Configure a Backend-Specific Quantizer
 
-This scheme represents 4-bit weight quantization with 8-bit dynamic quantization of activation during inference.
+Each backend provides its own quantizer (e.g., XNNPACKQuantizer, CoreMLQuantizer) that defines how quantization should be applied to a model in a way that is compatible with the target hardware.
+These quantizers usually support configs that allow users to specify quantization options such as:
 
-Imported with ``from torchao.quantization.quant_api import Int8DynActInt4WeightQuantizer``, this class uses a quantization instance constructed with a specified dtype precision and groupsize, to mutate a provided ``nn.Module``.
+* Precision (e.g., 8-bit or 4-bit)
+* Quantization type (e.g., dynamic, static, or weight-only quantization)
+* Granularity (e.g., per-tensor, per-channel)
 
-```
-# Source Quant
-from torchao.quantization.quant_api import Int8DynActInt4WeightQuantizer
+Not all quantization options are supported by all backends. Consult backend-specific guides for supported quantization modes and configuration, and how to initialize the backend-specific PT2E quantizer:
+
+* [XNNPACK quantization](backends-xnnpack.md#quantization)
+* [CoreML quantization](backends-coreml.md#quantization)
+
+
+
+## 2. Quantize and evaluate the model
+
+After the backend specific quantizer is defined, the PT2E quantization flow is the same for all backends.  A generic example is provided below, but specific examples are given in backend documentation:
+
+```python
+from torchao.quantization.pt2e.quantize_pt2e import convert_pt2e, prepare_pt2e
+
+training_gm = torch.export.export(model, sample_inputs).module()
 
-model = Int8DynActInt4WeightQuantizer(precision=torch_dtype, groupsize=group_size).quantize(model)
+# Prepare the model for quantization using the backend-specific quantizer instance
+prepared_model = prepare_pt2e(training_gm, quantizer)
 
-# Export to ExecuTorch
-from executorch.exir import to_edge
-from torch.export import export
 
-exported_model = export(model, ...)
-et_program = to_edge(exported_model, ...).to_executorch(...)
+# Calibrate the model on representative data
+for sample in calibration_data:
+	prepared_model(sample)
+
+# Convert the calibrated model to a quantized model
+quantized_model = convert_pt2e(prepared_model)
+```
+
+The quantized_model is a PyTorch model like any other, and can be evaluated on different tasks for accuracy.
+Tasks specific benchmarks are the recommended way to evaluate your quantized model, but as crude alternative you can compare to outputs with the original model using generic error metrics like SQNR:
+
+```python
+from torchao.quantization.utils import compute_error
+out_reference = model(sample)
+out_quantized = quantized_model(sample)
+sqnr = compute_error(out_reference, out_quantized) # SQNR error
 ```
+
+Note that numerics on device can differ those in PyTorch even for unquantized models, and accuracy evaluation can also be done with pybindings or on device.
+
+
+## 3. Lower the model
+
+The final step is to lower the quantized_model to the desired backend, as you would an unquantized one.  See [backend-specific pages](backends-overview.md) for lowering information.
diff --git a/docs/source/tutorial-arm-ethos-u.md b/docs/source/tutorial-arm-ethos-u.md
index bebd8ba9310..528bcd93452 100644
--- a/docs/source/tutorial-arm-ethos-u.md
+++ b/docs/source/tutorial-arm-ethos-u.md
@@ -334,9 +334,9 @@ Once the elf is prepared, regardless of the `.pte` file variant is used to gener
 ./run.sh --model_name=mv2 --delegate --target=ethos-u85-128
 ```
 
-- To run all the test models iteratively in a loop 
+- To run all the test models iteratively in a loop , simply run
 ```bash
-./run.sh --pte=mv2_arm_ethos_u55.pte --target=ethos-u55-128
+./run.sh
 ```
 
 Note that you could use `build_executor_runner.sh` and `run_fvp.sh` scripts in tandem by passing the relevant  --target argument (e.g., --target=ethos-u55-128), the correct FVP binary will be chosen automatically. For more details, see the [section on Runtime Integration](https://docs.pytorch.org/executorch/main/backends-arm-ethos-u.html#runtime-integration).
diff --git a/docs/source/tutorials_source/bundled_program.bp b/docs/source/tutorials_source/bundled_program.bp
index 4180e381bb3..8afe3cfee26 100644
Binary files a/docs/source/tutorials_source/bundled_program.bp and b/docs/source/tutorials_source/bundled_program.bp differ
diff --git a/docs/source/using-executorch-building-from-source.md b/docs/source/using-executorch-building-from-source.md
index a5518818263..eb997ab9ab7 100644
--- a/docs/source/using-executorch-building-from-source.md
+++ b/docs/source/using-executorch-building-from-source.md
@@ -42,7 +42,7 @@ portability details.
 
    ```bash
    # Clone the ExecuTorch repo from GitHub
-   git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+   git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
    ```
 
 ### Create a Virtual Environment
@@ -60,7 +60,7 @@ Or alternatively, [install conda on your machine](https://conda.io/projects/cond
 ## Install ExecuTorch pip package from source
    ```bash
    # Install ExecuTorch pip package and its dependencies, as well as
-   # development tools like CMake.
+   # development tools like CMake, and backend support for XNNPACK and CoreML.
    # If developing on a Mac, make sure to install the Xcode Command Line Tools first.
    # Intel-based macOS systems require building PyTorch from source (see below)
    ./install_executorch.sh
@@ -79,10 +79,16 @@ Or alternatively, [install conda on your machine](https://conda.io/projects/cond
    ./install_executorch.sh --use-pt-pinned-commit --minimal
    ```
 
-   Not all backends are built into the pip wheel by default. You can link these missing/experimental backends by turning on the corresponding cmake flag. For example, to include the MPS backend:
+  Notice that only XNNPACK and CoreML backends are supported by default. You can enable additional backends or disable default backends by setting the corresponding CMake flags:
 
   ```bash
+  # Enable the MPS backend
   CMAKE_ARGS="-DEXECUTORCH_BUILD_MPS=ON" ./install_executorch.sh
+  ```
+
+  ```bash
+  # Disable the XNNPACK backend
+  CMAKE_ARGS="-DEXECUTORCH_BUILD_XNNPACK=OFF" ./install_executorch.sh
   ```
 
    For development mode, run the command with `--editable`, which allows us to modify Python source code and see changes reflected immediately.
diff --git a/docs/source/using-executorch-cpp.md b/docs/source/using-executorch-cpp.md
index d64dad97da9..19051e23b45 100644
--- a/docs/source/using-executorch-cpp.md
+++ b/docs/source/using-executorch-cpp.md
@@ -6,7 +6,7 @@ In order to support a wide variety of devices, from high-end mobile phones down
 
 The C++ `Module` class provides the high-level interface to load and execute a model from C++. It is responsible for loading the .pte file, configuring memory allocation and placement, and running the model. The Module constructor takes a file path and provides a simplified `forward()` method to run the model.
 
-In addition the Module class, the tensor extension provides an encapsulated interface to define and manage tensor memory. It provides the `TensorPtr` class, which is a "fat" smart pointer. It provides ownership over the tensor  data and metadata, such as size and strides. The `make_tensor_ptr` and `from_blob` methods, defined in `tensor.h`, provide owning and non-owning tensor creation APIs, respectively.
+In addition to the Module class, the tensor extension provides an encapsulated interface to define and manage tensor memory. It provides the `TensorPtr` class, which is a "fat" smart pointer. It provides ownership over the tensor  data and metadata, such as size and strides. The `make_tensor_ptr` and `from_blob` methods, defined in `tensor.h`, provide owning and non-owning tensor creation APIs, respectively.
 
 ```cpp
 #include <executorch/extension/module/module.h>
@@ -40,7 +40,7 @@ Running a model using the low-level runtime APIs allows for a high-degree of con
 
 ## Building with CMake
 
-ExecuTorch uses CMake as the primary build system. Inclusion of the module and tensor APIs are controlled by the `EXECUTORCH_BUILD_EXTENSION_MODULE` and `EXECUTORCH_BUILD_EXTENSION_TENSOR` CMake options. As these APIs may not be supported on embedded systems, they are disabled by default when building from source. The low-level API surface is always included. To link, add the `executorch` target as a CMake dependency, along with `extension_module_static` and `extension_tensor`, if desired.
+ExecuTorch uses CMake as the primary build system. Inclusion of the module and tensor APIs are controlled by the `EXECUTORCH_BUILD_EXTENSION_MODULE` and `EXECUTORCH_BUILD_EXTENSION_TENSOR` CMake options. As these APIs may not be supported on embedded systems, they are disabled by default when building from source. The low-level API surface is always included. To link, add the `executorch` target as a CMake dependency, along with `extension_module_static` and `extension_tensor`, if desired. Note that `extension_flat_tensor` is required as a dependency of `extension_module` for [program-data separation support](ptd-file-format.md).
 
 ```
 # CMakeLists.txt
@@ -50,6 +50,7 @@ target_link_libraries(
     my_target
     PRIVATE executorch
     extension_module_static
+    extension_flat_tensor
     extension_tensor
     optimized_native_cpu_ops_lib
     xnnpack_backend)
diff --git a/docs/source/using-executorch-export.md b/docs/source/using-executorch-export.md
index 914d4b7d315..da9cadf3ec2 100644
--- a/docs/source/using-executorch-export.md
+++ b/docs/source/using-executorch-export.md
@@ -1,6 +1,6 @@
 # Model Export and Lowering
 
-The section describes the process of taking a PyTorch model and converting to the runtime format used by ExecuTorch. This process is commonly known as "exporting", as it uses the PyTorch export functionality to convert a PyTorch model into a format suitable for on-device execution. This process yields a .pte file which is optimized for on-device execution using a particular backend.
+The section describes the process of taking a PyTorch model and converting to the runtime format used by ExecuTorch. This process is commonly known as "exporting", as it uses the PyTorch export functionality to convert a PyTorch model into a format suitable for on-device execution. This process yields a .pte file which is optimized for on-device execution using a particular backend. If using program-data separation, it also yields a corresponding .ptd file containing only the weights/constants from the model.
 
 ## Prerequisites
 
@@ -30,7 +30,7 @@ As part of the .pte file creation process, ExecuTorch identifies portions of the
 
 ### Available Backends
 
-Commonly used hardware backends are listed below. For mobile, consider using XNNPACK for Android and XNNPACK or Core ML for iOS. To create a .pte file for a specific backend, pass the appropriate partitioner class to `to_edge_transform_and_lower`. See the appropriate backend documentation and the [Export and Lowering](#export-and-lowering) section below for more information. 
+Commonly used hardware backends are listed below. For mobile, consider using XNNPACK for Android and XNNPACK or Core ML for iOS. To create a .pte file for a specific backend, pass the appropriate partitioner class to `to_edge_transform_and_lower`. See the appropriate backend documentation and the [Export and Lowering](#export-and-lowering) section below for more information.
 
 - [XNNPACK (Mobile CPU)](backends-xnnpack.md)
 - [Core ML (iOS)](backends-coreml.md)
@@ -61,7 +61,7 @@ class Model(torch.nn.Module):
             torch.nn.AdaptiveAvgPool2d((1,1))
        )
         self.linear = torch.nn.Linear(16, 10)
-    
+
     def forward(self, x):
         y = self.seq(x)
         y = torch.flatten(y, 1)
@@ -97,7 +97,7 @@ class Model(torch.nn.Module):
             torch.nn.AdaptiveAvgPool2d((1,1))
         )
         self.linear = torch.nn.Linear(16, 10)
-    
+
     def forward(self, x):
         y = self.seq(x)
         y = torch.flatten(y, 1)
@@ -125,6 +125,31 @@ with open("model.pte", "wb") as file:
 
 This yields a `model.pte` file which can be run on mobile devices.
 
+To generate a `model.pte`, `model.ptd` pair with the weights inside `model.ptd`, add the following transform function to tag constants as external:
+
+```python
+from executorch.exir.passes.external_constants_pass import (
+    delegate_external_constants_pass,
+)
+partial_function = partial(
+    delegate_external_constants_pass,
+    ep=exported_program,
+    gen_tag_fn=lambda x: "model", # This is the filename the weights will be saved to. In this case, weights will be saved as "model.ptd"
+)
+
+executorch_program = to_edge_transform_and_lower(
+    exported_program,
+    transform_passes = [partial_function],
+    partitioner = [XnnpackPartitioner()]
+).to_executorch()
+```
+
+To save the PTD file:
+```
+executorch_program.write_tensor_data_to_file(output_directory)
+```
+It will be saved to the file `model.ptd`, with the file name coming from `gen_tag_fn` in the transform pass.
+
 ### Supporting Varying Input Sizes (Dynamic Shapes)
 
 The PyTorch export process uses the example inputs provided to trace through the model and reason about the size and type of tensors at each step. Unless told otherwise, export will assume a fixed input size equal to the example inputs and will use this information to optimize the model.
@@ -167,6 +192,8 @@ method = program.load_method("forward")
 outputs = method.execute([input_tensor])
 ```
 
+Pybindings currently does not support loading program and data. To run a model with PTE and PTD components, please use the [Extension Module](extension-module.md). There is also an E2E demo in [executorch-examples](https://github.com/pytorch-labs/executorch-examples/tree/main/program-data-separation).
+
 For more information, see [Runtime API Reference](executorch-runtime-api-reference.md).
 
 ## Advanced Topics
@@ -227,7 +254,7 @@ class EncodeWrapper(torch.nn.Module):
     def __init__(self, model):
         super().__init__()
         self.model = model
-    
+
     def forward(self, *args, **kwargs):
         return self.model.encode(*args, **kwargs)
 
diff --git a/docs/source/using-executorch-faqs.md b/docs/source/using-executorch-faqs.md
index f639524d69c..d1bd0390569 100644
--- a/docs/source/using-executorch-faqs.md
+++ b/docs/source/using-executorch-faqs.md
@@ -14,6 +14,13 @@ sudo apt install python<version>-dev
 ```
 if you are using Ubuntu, or use an equivalent install command.
 
+### ModuleNotFoundError: No module named 'pytorch_tokenizers'
+
+The `pytorch_tokenizers` package is required for LLM export functionality. Install it from the ExecutorTorch source code:
+```
+pip install -e ./extension/llm/tokenizers/
+```
+
 ## Export
 
 ### Missing out variants: { _ }
diff --git a/docs/source/using-executorch-ios.md b/docs/source/using-executorch-ios.md
index e3668a29e33..249e80ddd06 100644
--- a/docs/source/using-executorch-ios.md
+++ b/docs/source/using-executorch-ios.md
@@ -6,7 +6,7 @@ ExecuTorch supports both iOS and macOS via Objective-C, Swift, and C++. ExecuTor
 
 The ExecuTorch Runtime for iOS and macOS (ARM64) is distributed as a collection of prebuilt [.xcframework](https://developer.apple.com/documentation/xcode/creating-a-multi-platform-binary-framework-bundle) binary targets. These targets are compatible with both iOS and macOS devices and simulators and are available in both release and debug modes:
 
-* `executorch` - Main Runtime components
+* `executorch` - Core runtime components
 * `backend_coreml` - Core ML backend
 * `backend_mps` - MPS backend
 * `backend_xnnpack` - XNNPACK backend
@@ -24,7 +24,7 @@ The prebuilt ExecuTorch runtime, backend, and kernels are available as a [Swift
 
 #### Xcode
 
-In Xcode, go to `File > Add Package Dependencies`. Paste the URL of the [ExecuTorch repo](https://github.com/pytorch/executorch) into the search bar and select it. Make sure to change the branch name to the desired ExecuTorch version in format "swiftpm-<version>", (e.g. "swiftpm-0.6.0"), or a branch name in format "swiftpm-<version>.<year_month_date>" (e.g. "swiftpm-0.7.0-20250401") for a [nightly build](https://ossci-ios.s3.amazonaws.com/list.html) on a specific date.
+In Xcode, go to `File > Add Package Dependencies`. Paste the URL of the [ExecuTorch repo](https://github.com/pytorch/executorch) into the search bar and select it. Make sure to change the branch name to the desired ExecuTorch version in format "swiftpm-<version>", (e.g. "swiftpm-0.7.0"), or a branch name in format "swiftpm-<version>.<year_month_date>" (e.g. "swiftpm-0.8.0-20250801") for a [nightly build](https://ossci-ios.s3.amazonaws.com/list.html) on a specific date.
 
 ![](_static/img/swiftpm_xcode1.png)
 
@@ -57,7 +57,7 @@ let package = Package(
   ],
   dependencies: [
     // Use "swiftpm-<version>.<year_month_day>" branch name for a nightly build.
-    .package(url: "https://github.com/pytorch/executorch.git", branch: "swiftpm-0.6.0")
+    .package(url: "https://github.com/pytorch/executorch.git", branch: "swiftpm-0.7.0")
   ],
   targets: [
     .target(
@@ -96,7 +96,7 @@ xcode-select --install
 2. Clone ExecuTorch:
 
 ```bash
-git clone -b viable/strict https://github.com/pytorch/executorch.git --depth 1 --recurse-submodules --shallow-submodules && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git --depth 1 --recurse-submodules --shallow-submodules && cd executorch
 ```
 
 3. Set up [Python](https://www.python.org/downloads/macos/) 3.10+ and activate a virtual environment:
@@ -124,11 +124,7 @@ sudo /Applications/CMake.app/Contents/bin/cmake-gui --install
 
 6. Use the provided script to build .xcframeworks:
 
-```bash
-./scripts/build_apple_frameworks.sh --help
-```
-
-For example, the following command will build the ExecuTorch Runtime along with all available kernels and backends for the Apple platform in both Release and Debug modes:
+The following command will build the ExecuTorch runtime components along with all available kernels and backends for the Apple platform in both Release and Debug modes:
 
 ```bash
 ./scripts/build_apple_frameworks.sh
@@ -220,7 +216,7 @@ ExecuTorchTensor *inputTensor = [[ExecuTorchTensor alloc] initWithBytesNoCopy:im
 NSArray<ExecuTorchValue *> *outputs = [module forwardWithTensor:inputTensor error:&error];
 
 // Get the first output value assuming it's a tensor.
-ExecuTorchTensor *outputTensor = outputs.firstObject.tensor;
+ExecuTorchTensor *outputTensor = outputs.firstObject.tensorValue;
 
 // Access the output tensor data.
 [outputTensor bytesWithHandler:^(const void *pointer, NSInteger count, ExecuTorchDataType dataType) {
@@ -243,74 +239,112 @@ try module.load("forward")
 let imageBuffer: UnsafeMutableRawPointer = ... // Existing image buffer
 
 // Create an input tensor referencing the buffer and assuming the given shape and data type.
-let inputTensor = Tensor(
-  bytesNoCopy: imageBuffer,
-  shape: [1, 3, 224, 224],
-  dataType: .float
-)
+let inputTensor = Tensor<Float>(&imageBuffer, shape: [1, 3, 224, 224])
 
-// Execute the 'forward' method with the given input tensor and get output values back.
-let outputs = try module.forward(inputTensor)
+// Execute the 'forward' method with the given input tensor and get an output tensor back.
+let outputTensor: Tensor<Float> = try module.forward(inputTensor)[0].tensor()!
 
-// Get the first output value assuming it's a tensor.
-if let outputTensor = outputs.first?.tensor {
-  // Access the output tensor data.
-  outputTensor.bytes { pointer, count, dataType in
-    // Copy the tensor data into logits array for easier access.
-    let logits = Array(UnsafeBufferPointer(
-      start: pointer.assumingMemoryBound(to: Float.self),
-      count: count
-    ))
-    // Use logits...
-  }
-}
+// Copy the tensor data into logits array for easier access.
+let logits = try outputTensor.scalars()
+
+// Use logits...
 ```
 
 ### Tensor
 
-The `Tensor` class (exposed as `ExecuTorchTensor` in Objective-C) represents a multi-dimensional array of elements (such as floats or ints) and includes metadata like shape (dimensions) and data type. Tensors are used to feed inputs to a model and retrieve outputs, or for any computation you need to do on raw data. You can create tensors from simple arrays of numbers, inspect their properties, read or modify their contents, and even reshape or copy them.
+A tensor is a multi-dimensional array of elements (such as floats or integers) and includes metadata like shape (dimensions) and data type. Tensors are used to feed inputs to a model and retrieve outputs, or for any computation you need to do on raw data. You can create tensors from simple arrays of numbers, inspect their properties, read or modify their contents, and even reshape or copy them.
+
+ExecuTorch offers `ExecuTorchTensor` class in Objective-C and two tensor types in Swift:
+
+- `AnyTensor`: A type-erased tensor, bridged from `ExecuTorchTensor` in Objective-C. You might use it when the tensor's data type is only known at runtime, for example, when converting from an untyped `Value` object before casting it to a generic `Tensor<T>`.
+
+- `Tensor<T: Scalar>`: A generic, type-safe wrapper around AnyTensor. This is the recommended type for most use cases in Swift. It ensures the element type (e.g., `Float`, `Int`) is known at compile time, providing type-safe access to tensor data and catching type mismatches early.
+
+You can convert between them using `tensor.anyTensor` (to get the underlying `AnyTensor`) and `anyTensor.asTensor()` (to convert to a typed `Tensor<T>` if the data types match).
 
 #### Key Properties:
 
-- dataType: The element type (e.g., `.float`, `.int`, `.byte`).
-- shape: An array of `NSNumber` describing the size of each dimension.
-- count: The total number of elements.
-- strides: The jump in memory needed to advance one element along each dimension.
-- dimensionOrder: The order of dimensions in memory.
-- shapeDynamism: Indicates if the tensor shape can change (`.static`, `.dynamicBound`, `.dynamicUnbound`).
+- `dataType`: The element type (e.g., `.float`, `.int`, `.byte`). In `Tensor<T>`, this is determined by `T`.
+- `shape`: An array of `Int` describing the size of each dimension.
+- `count`: The total number of elements.
+- `strides`: The jump in memory needed to advance one element along each dimension.
+- `dimensionOrder`: The order of dimensions in memory.
+- `shapeDynamism`: Indicates if the tensor shape can change (`.static`, `.dynamicBound`, `.dynamicUnbound`).
 
 #### Initialization:
 
-You can create tensors in various ways:
+You can create a new tensor from an existing one, either as a view (which shares the same underlying data) or as a copy (which gets its own unique data).
 
-From existing memory buffers:
-- `init(bytesNoCopy:shape:dataType:...)`: Creates a tensor that references an existing memory buffer without copying. The buffer's lifetime must exceed the tensor's.
-- `init(bytes:shape:dataType:...)`: Creates a tensor by copying data from a memory buffer.
+- View: `init(_:)` creates a new tensor instance that points to the same memory as the original. Modifying the data through one tensor will affect the other.
 
-From `NSData` / `Data`:
-- `init(data:shape:dataType:...)`: Creates a tensor using an `NSData` object, referencing its bytes without copying.
+- Copy: `copy()` creates a completely independent duplicate of the tensor, including its own copy of the data.
 
-From scalar arrays:
-- `init(_:shape:dataType:...)`: Creates a tensor from an array of `NSNumber` scalars. Convenience initializers exist to infer shape or data type.
+Objective-C:
+```objectivec
+// Create a view.
+ExecuTorchTensor *tensorView = [[ExecuTorchTensor alloc] initWithTensor:originalTensor];
 
-From single scalars:
-- `init(_:)`, `init(_:dataType:)`, `init(float:)`, `init(int:)`, etc.: Create 0-dimensional tensors (scalars).
+// Create a copy.
+ExecuTorchTensor *tensorCopy = [originalTensor copy];
+```
 
-Objective-C:
+Swift:
+```swift
+// Create a view.
+let tensorView = Tensor(originalTensor)
 
-```objectivec
-#import <ExecuTorch/ExecuTorch.h>
+// Create a copy.
+let tensorCopy = originalTensor.copy()
+```
+Tensors can be initialized directly from memory pointers or `Data` objects.
 
-// Create from copying bytes.
+- `init(bytesNoCopy:...)`: Creates a tensor that references an existing memory buffer without copying. The buffer's lifetime must be managed manually and must exceed the tensor's.
+
+- `init(bytes:...)`: Creates a tensor by copying data from a memory buffer.
+
+- `init(data:...)`: Creates a tensor using an `NSData` (Objective-C) or `Data` (Swift) object, referencing its bytes without copying.
+
+Objective-C:
+```objectivec
+// Create by copying bytes.
 float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
 NSArray<NSNumber *> *shape = @[@2, @2];
 ExecuTorchTensor *tensorFromBytes = [[ExecuTorchTensor alloc] initWithBytes:data
                                                                       shape:shape
                                                                    dataType:ExecuTorchDataTypeFloat];
 
-// Create from scalars.
+// Create from NSData (no copy).
+NSData *nsData = [NSData dataWithBytes:data length:sizeof(data)];
+ExecuTorchTensor *tensorFromNSData = [[ExecuTorchTensor alloc] initWithData:nsData
+                                                                      shape:shape
+                                                                   dataType:ExecuTorchDataTypeFloat];
+```
+
+Swift:
+```swift
+// Create from a buffer without copying (unsafe).
+var mutableData: [Float] = [1.0, 2.0, 3.0, 4.0]
+let tensorNoCopy = mutableData.withUnsafeMutableBytes { pointer in
+  Tensor<Float>(
+    bytesNoCopy: pointer.baseAddress!,
+    shape: [2, 2]
+  )
+}
+
+// Create from Data (no copy).
+let data = Data(bytes: &mutableData, count: mutableData.count * MemoryLayout<Float>.size)
+let tensorFromData = Tensor<Float>(data: data, shape: [2, 2])
+```
+
+The most convenient way to create tensors is from Swift arrays or single scalar values. The `Tensor<T>` API uses type inference to determine the `dataType` automatically.
+
+objective-c:
+```objectivec
+// Create from an array of scalars.
 NSArray<NSNumber *> *scalars = @[@(1), @(2), @(3)];
+NSArray<NSNumber *> *shape = @[@3];
 ExecuTorchTensor *tensorFromScalars = [[ExecuTorchTensor alloc] initWithScalars:scalars
+                                                                          shape:shape
                                                                        dataType:ExecuTorchDataTypeInt];
 
 // Create a float scalar tensor.
@@ -319,71 +353,150 @@ ExecuTorchTensor *scalarTensor = [[ExecuTorchTensor alloc] initWithFloat:3.14f];
 
 Swift:
 ```swift
-import ExecuTorch
+// Create from an array of scalars (infers shape and copies data).
+let tensor = Tensor([1.0, 2.0, 3.0, 4.0]) // Creates a Tensor<Double> with shape [4]
 
-// Create from existing buffer without copying.
-var mutableData: [Float] = [1.0, 2.0, 3.0, 4.0]
-let tensorNoCopy = mutableData.withUnsafeMutableBytes { bufferPointer in
-  Tensor(
-    bytesNoCopy: bufferPointer.baseAddress!,
-    shape: [2, 2],
-    dataType: .float
-  )
-}
-
-// Create from Data (no copy).
-let data = Data(bytes: mutableData, count: mutableData.count * MemoryLayout<Float>.size)
-let tensorFromData = Tensor(data: data, shape: [2, 2], dataType: .float)
+// Specify shape.
+let tensorWithShape = Tensor([1, 2, 3, 4, 5, 6], shape: [2, 3]) // Creates Tensor<Int>
 
-// Create from scalars (infers float type).
-let tensorFromScalars = Tensor([1.0, 2.0, 3.0, 4.0], shape: [4])
+// Create without copying from an `inout` array.
+var liveData: [Int32] = [10, 20, 30]
+let tensorNoCopy = Tensor(&liveData) // Modifying `liveData` affects `tensorNoCopy`
 
 // Create an Int scalar tensor.
-let scalarTensor = Tensor(42) // Infers Int as .long data type (64-bit integer)
+let scalarTensor = Tensor(42) // Infers Tensor<Int> with shape []
 ```
 
-#### Accessing Data:
+#### Factory Methods:
+
+ExecuTorch provides a rich set of factory methods to create tensors with pre-filled or random data.
+
+- `empty`: Creates a tensor with uninitialized data.
 
-Use `bytes(_:)` for immutable access and `mutableBytes(_:)` for mutable access to the tensor's underlying data buffer.
+- `full`: Creates a tensor filled with a specified scalar value.
+
+- `ones`: Creates a tensor filled with ones.
+
+- `zeros`: Creates a tensor filled with zeros.
+
+- `rand`: Creates a tensor with random values uniformly distributed in `[0, 1)`.
+
+- `randn`: Creates a tensor with random values from a normal distribution (mean 0, variance 1).
+
+- `randint`: Creates a tensor with random integers in a specified range `[low, high)`.
+
+Each method has a `like:` variant that creates a new tensor with the same shape and properties as an existing one.
 
 Objective-C:
+```objectivec
+// Create a 2x2 tensor filled with zeros.
+ExecuTorchTensor *zeros = [ExecuTorchTensor zerosTensorWithShape:@[@2, @2]
+                                                        dataType:ExecuTorchDataTypeFloat];
+
+// Create a tensor of ones with the same shape as `zeros`.
+ExecuTorchTensor *ones = [ExecuTorchTensor onesTensorLikeTensor:zeros];
+```
+
+Swift:
+```swift
+// Create a 2x2 tensor filled with the value 7.
+let fullTensor = Tensor<Int32>.full(shape: [2, 2], scalar: 7)
+
+// Create a 3x3 tensor of ones.
+let onesTensor = Tensor<Float>.ones(shape: [3, 3])
+
+// Create a tensor of zeros with the same shape as onesTensor.
+let zerosTensor = Tensor<Float>.zeros(like: onesTensor)
+
+// Create a tensor with random integers between 10 (inclusive) and 20 (exclusive).
+let randomInts = Tensor<Int>.randint(low: 10, high: 20, shape: [5])
+
+// Create a 2x2 type-erased tensor filled with zeros and explicit data type.
+let anyZeros = AnyTensor.zeros(shape: [2, 2], dataType: .float)
+
+// Create a 2x3 type-erased tensor filled with random values and explicit data type.
+let anyRand = AnyTensor.rand(shape: [2, 3], dataType: .double)
+```
+
+#### Accessing Data:
+
+Reading data:
+
+- `scalars()`: Returns a copy of the tensor's elements as a new `[T]` array.
 
+- `withUnsafeBytes(_:)`: Provides a type-safe, immutable buffer pointer (`UnsafeBufferPointer<T>`) for efficient, direct memory access without creating a new array.
+
+- `bytesWithHandler:`: The Objective-C and `AnyTensor` approach, which uses a callback with a raw `void *` pointer and requires manual type casting.
+
+Objective-C:
 ```objectivec
 [tensor bytesWithHandler:^(const void *pointer, NSInteger count, ExecuTorchDataType dataType) {
   if (dataType == ExecuTorchDataTypeFloat) {
-    const float *floatPtr = (const float *)pointer;
-    NSLog(@"First float element: %f", floatPtr[0]);
+    const float *floatPointer = (const float *)pointer;
+    NSLog(@"First float element: %f", floatPointer[0]);
   }
 }];
+```
+
+Swift:
+```swift
+let tensor = Tensor<Float>([1.0, 2.0, 3.0, 4.0], shape: [2, 2])
+
+// Get data copy as a Swift array.
+let scalars = try tensor.scalars()
+print("All scalars: \(scalars)") // [1.0, 2.0, 3.0, 4.0]
+
+// Access data via a buffer pointer.
+try tensor.withUnsafeBytes { buffer in
+  print("First float element: \(buffer.first ?? 0.0)")
+}
+
+anyTensor.bytes { pointer, count, dataType in
+  // Must check data type and manually cast the pointer for type-erased tensor.
+  if dataType == .float {
+    let buffer = UnsafeBufferPointer(start: pointer.assumingMemoryBound(to: Float.self), count: count)
+    print("First float element from AnyTensor: \(buffer.first ?? 0.0)")
+  }
+}
+```
+
+Modifying Data:
 
+- `withUnsafeMutableBytes(_:)`: The preferred Swift method. Provides a type-safe, mutable buffer pointer (`UnsafeMutableBufferPointer<T>`) for in-place modification.
+
+- `mutableBytesWithHandler:`: The Objective-C and `AnyTensor` equivalent.
+
+Objective-C:
+```objectivec
 [tensor mutableBytesWithHandler:^(void *pointer, NSInteger count, ExecuTorchDataType dataType) {
   if (dataType == ExecuTorchDataTypeFloat) {
-    float *floatPtr = (float *)pointer;
-    floatPtr[0] = 100.0f; // Modify the original mutableData buffer.
+    float *floatPointer = (float *)pointer;
+    floatPointer[0] = 100.0f; // Modify the tensor's data.
   }
 }];
 ```
 
 Swift:
 ```swift
-tensor.bytes { pointer, count, dataType in
-  if dataType == .float {
-    let buffer = UnsafeBufferPointer(start: pointer.assumingMemoryBound(to: Float.self), count: count)
-    print("First float element: \(buffer.first ?? 0.0)")
-  }
+let tensor = Tensor<Float>([1.0, 2.0, 3.0, 4.0], shape: [2, 2])
+
+// Modify the tensor's data in place.
+try tensor.withUnsafeMutableBytes { buffer in
+  buffer[1] = 200.0
 }
+// tensor's data is now [1.0, 200.0, 3.0, 4.0]
 
-tensor.mutableBytes { pointer, count, dataType in
+anyTensor.mutableBytes { pointer, count, dataType in
   if dataType == .float {
     let buffer = UnsafeMutableBufferPointer(start: pointer.assumingMemoryBound(to: Float.self), count: count)
-    buffer[1] = 200.0 // Modify the original mutableData buffer.
+    buffer[0] = 100.0 // Modify the AnyTensor's data
   }
 }
 ```
 
 #### Resizing:
 
-Tensors can be resized if their underlying memory allocation allows it (typically requires ShapeDynamism other than Static or sufficient capacity).
+Tensors can be resized if their shape dynamism is not `.static`. Resizing only changes the tensor's metadata (shape and strides) and does not reallocate or change the underlying data, so the new shape must have the same total number of elements.
 
 Objective-C:
 
@@ -407,6 +520,14 @@ do {
 }
 ```
 
+#### Equality:
+
+You can check if two tensors are equal using the `==` operator. It compares their data type, shape, strides, dimension order, and all underlying element data. The `shapeDynamism` property is disregarded in this comparison.
+
+#### Printing:
+
+Tensors conform to `CustomStringConvertible` in Swift and implement `-description` in Objective-C, so you can print them directly to the console for easy debugging.
+
 ### Value
 
 The `Value` class (exposed as `ExecuTorchValue` in Objective-C) is a dynamic container that can hold different types of data, primarily used for model inputs and outputs. ExecuTorch methods accept and return arrays of `Value` objects.
@@ -449,13 +570,29 @@ let boolValue = Value(false)
 let doubleValue = Value(2.718)
 ```
 
+Also, in Swift, all the types that `Value` can hold conform to the `ValueConvertible` protocol, so you can create `Value` objects directly from them without explicitly wrapping them in `Value` constructors:
+
+```swift
+func processValue(_ value: ValueConvertible) {
+  // ...
+}
+
+processValue(1) // Value<Int>
+processValue(1.0) // Value<Double>
+processValue("hello") // Value<String>
+processValue(true) // Value<Bool>
+processValue(Tensor(1.0)) // Value<Tensor>
+```
+
 ### Module
 
-The `Module` class (exposed as `ExecuTorchModule` in Objective-C) represents a loaded ExecuTorch model (`.pte` file). It provides methods to load the model program and execute its internal methods (like `forward`).
+The `Module` class (exposed as `ExecuTorchModule` in Objective-C) represents a loaded ExecuTorch model (`.pte` file). It provides methods to load the model program, inspect its methods, and execute them for inference.
+
+Note: `Module` and its methods are not thread-safe. If you need to do concurrent inferences from multiple threads, create one `Module` per thread.
 
 #### Initialization:
 
-Create a `Module` instance by providing the file path to the `.pte` model. Initialization itself is lightweight and doesn't load the program data immediately.
+Create a `Module` instance by providing the file path to the `.pte` model. Initialization itself is lightweight and doesn't load the program data immediately. You can also specify a `ModuleLoadMode` to control how the file is loaded, such as using memory mapping for efficiency.
 
 Objective-C:
 
@@ -464,26 +601,28 @@ Objective-C:
 
 NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model" ofType:@"pte"];
 ExecuTorchModule *module = [[ExecuTorchModule alloc] initWithFilePath:modelPath];
+
 // Optional: specify load mode, e.g., memory mapping.
-// ExecuTorchModule *moduleMmap = [[ExecuTorchModule alloc] initWithFilePath:modelPath
-//                                                                   loadMode:ExecuTorchModuleLoadModeMmap];
+ExecuTorchModule *moduleMmap = [[ExecuTorchModule alloc] initWithFilePath:modelPath
+                                                                 loadMode:ExecuTorchModuleLoadModeMmap];
 ```
 
 Swift:
 ```swift
 import ExecuTorch
 
-let modelPath = Bundle.main.path(forResource: "model", ofType: "pte")
-let module = Module(filePath: modelPath!)
+let modelPath = Bundle.main.path(forResource: "model", ofType: "pte")!
+let module = Module(filePath: modelPath)
+
 // Optional: specify load mode, e.g., memory mapping.
-// let moduleMmap = Module(filePath: modelPath, loadMode: .mmap)
+let moduleMmap = Module(filePath: modelPath, loadMode: .mmap)
 ```
 
 #### Loading:
 
-Model loading is deferred until explicitly requested or needed for execution. While execution calls can trigger loading automatically, it's often more efficient to load methods explicitly beforehand.
+Model loading is deferred until explicitly requested or needed. You can load the entire program or individual methods. While execution calls can trigger loading automatically, it's often more efficient to load methods explicitly beforehand.
 
-- `load()`: Loads the basic program structure. Minimal verification is used by default.
+- `load()`: Loads the basic program structure. You can specify a `ModuleVerification` level, though minimal verification is used by default.
 - `load(_:)`: Loads the program structure and prepares a specific method (e.g., "forward") for execution. This performs necessary setup like backend delegation and is recommended if you know which method you'll run.
 - `isLoaded()` / `isLoaded(_:)`: Check loading status.
 
@@ -512,18 +651,65 @@ do {
 }
 ```
 
+#### Inspecting Method Metadata
+
+You can programmatically inspect a method's contract—its input/output types, tensor shapes, data types, and more—by retrieving its MethodMetadata. This is incredibly useful for building dynamic applications that can adapt to different models without hardcoding dimensions.
+
+Objective-c:
+```objectivec
+NSError *error;
+ExecuTorchMethodMetadata *metadata = [module methodMetadata:@"forward" error:&error];
+
+if (metadata) {
+  // Check if the first input is a tensor.
+  ExecuTorchValueTag firstInputTag = [metadata.inputValueTags[0] unsignedIntValue];
+  if (firstInputTag == ExecuTorchValueTagTensor) {
+    // Get the metadata for the first input tensor.
+    ExecuTorchTensorMetadata *tensorMeta = metadata.inputTensorMetadata[@0];
+    if (tensorMeta) {
+      NSLog(@"Expected input shape: %@", tensorMeta.shape);
+      NSLog(@"Expected input data type: %ld", (long)tensorMeta.dataType);
+      // You can now dynamically create a matching input tensor.
+    }
+  }
+}
+```
+
+Swift:
+```swift
+do {
+  // Easily inspect the "forward" method at runtime.
+  let metadata = try module.methodMetadata("forward")
+
+  // Check if the first input is a tensor and get its metadata.
+  if metadata.inputValueTags.first == .tensor,
+    let tensorMeta = metadata.inputTensorMetadata[0] {
+
+    print("Expected input shape: \(tensorMeta.shape)")
+    print("Expected input data type: \(tensorMeta.dataType)")
+
+    // Dynamically create a random tensor that matches the model's input specs.
+    let input = AnyTensor.rand(shape: tensorMeta.shape, dataType: tensorMeta.dataType)
+
+    // Use the dynamically created tensor for inference.
+    let outputs = try module.forward(input)
+    print("Successfully ran inference with dynamic input.")
+  }
+} catch {
+  print("Failed to get metadata or run inference: \(error)")
+}
+```
+
 #### Execution:
 
-The `Module` class offers flexible ways to execute methods within the loaded program.
+The Module class offers flexible ways to execute methods.
+Inputs can be any type conforming to `ValueConvertible` (like `Tensor`, `Int`, `Float`, `Bool`, etc.).
+
+- `execute(_:_:)`: Execute any available method by name with one or more inputs.
 
-- Named Execution: You can execute any available method by name using `execute(methodName:inputs:)`.
-- Forward Shortcut: For the common case of running the primary inference method, use the `forward(inputs:)` shortcut, which is equivalent to calling execute with the method name "forward".
-- Input Flexibility: Inputs can be provided in several ways:
-  - As an array of `Value` objects. This is the most general form.
-  - As an array of `Tensor` objects. This is a convenience where tensors are automatically wrapped into `Value` objects.
-  - As a single `Value` or `Tensor` object if the method expects only one input.
-  - With no inputs if the method takes none.
+- `forward(_:)`: A convenient shortcut for executing the common "forward" method.
 
+The API provides overloads for single inputs, multiple inputs, or no inputs.
 Outputs are always returned as an array of `Value`.
 
 Objective-C:
@@ -545,9 +731,9 @@ if (outputs1) {
 // Execute "forward" with a single Tensor input.
 NSArray<ExecuTorchValue *> *outputs2 = [module forwardWithTensor:singleInputTensor error:&error];
 if (outputs2) {
-    NSLog(@"Forward single input output count: %lu", (unsigned long)outputs2.count);
+  NSLog(@"Forward single input output count: %lu", (unsigned long)outputs2.count);
 } else {
-    NSLog(@"Execution failed: %@", error);
+  NSLog(@"Execution failed: %@", error);
 }
 
 // Execute a potentially different method by name.
@@ -568,9 +754,9 @@ if (outputs1) {
 Swift:
 
 ```swift
-let inputTensor1 = Tensor([1.0, 2.0], dataType: .float)
-let inputTensor2 = Tensor([3.0, 4.0], dataType: .float)
-let singleInputTensor = Tensor([5.0], dataType: .float)
+let inputTensor1 = Tensor<Float>([1.0, 2.0])
+let inputTensor2 = Tensor<Float>([3.0, 4.0])
+let singleInputTensor = Tensor<Float>([5.0])
 
 do {
   // Execute "forward" using the shortcut with an array of Tensors.
@@ -582,13 +768,13 @@ do {
   print("Forward single input output count: \(outputs2.count)")
 
   // Execute a potentially different method by name.
-  let outputs3 = try module.execute("another_method", inputs: [Value(inputTensor1)])
+  let outputs3 = try module.execute("another_method", [inputTensor1])
 
-  // Process outputs (assuming first output is a tensor).
-  if let resultTensor = outputs1.first?.tensor {
-    resultTensor.bytes { ptr, count, dtype in
-      // Access result data.
-    }
+  // Process outputs by converting the first output Value to a typed Tensor<Float>.
+  if let outputTensor: Tensor<Float> = outputs1.first?.tensor() {
+    // Now you have a type-safe tensor and can access its data easily.
+    let logits = try outputTensor.scalars()
+    print("First 5 logits: \(logits.prefix(5))")
   }
 } catch {
   print("Execution failed: \(error)")
diff --git a/docs/source/using-executorch-runtime-integration.md b/docs/source/using-executorch-runtime-integration.md
index 550cb3eb71a..ed57142d8a7 100644
--- a/docs/source/using-executorch-runtime-integration.md
+++ b/docs/source/using-executorch-runtime-integration.md
@@ -18,13 +18,13 @@ cmake -b cmake-out -DEXECUTORCH_ENABLE_LOGGING=ON -DEXECUTORCH_LOG_LEVEL=DEBUG .
 
 ## Platform Abstraction Layer (PAL)
 
-The ExecuTorch Platform Abstraction Layer, or PAL, is a glue layer responsible for providing integration with a particular host system. This includes log routing, timestamps, and abort handling. ExecuTorch provides a default implementation for POSIX-compliant targets, as well as a Android and iOS-specific implementations under the appropriate extensions.
+The ExecuTorch Platform Abstraction Layer, or PAL, is a glue layer responsible for providing integration with a particular host system. This includes log routing, timestamps, and abort handling. ExecuTorch provides a default implementation for POSIX-compliant targets, as well as Android and iOS-specific implementations under the appropriate extensions.
 
 For non-POSIX-compliant systems, a minimal no-op PAL implementation is provided. It is expected that users override the relevant PAL methods in order to enable logging, timestamps, and aborts. The minimal PAL can be selected by building with `-DEXECUTORCH_PAL_DEFAULT=minimal`.
 
 ### Overriding the PAL
 
-Overriding the default PAL implementation is commonly done to route logs to a user-specified destination or to provide PAL functionality on embedded systems. The PAL can be overriden usinn runtime APIs or at link time. Prefer the runtime API unless you specifically need link-time overrides.
+Overriding the default PAL implementation is commonly done to route logs to a user-specified destination or to provide PAL functionality on embedded systems. The PAL can be overriden using runtime APIs or at link time. Prefer the runtime API unless you specifically need link-time overrides.
 
 ### Runtime PAL Registration
 
@@ -84,7 +84,7 @@ See [runtime/platform/platform.h](https://github.com/pytorch/executorch/blob/mai
 
 During export, a model is broken down into a list of operators, each providing some fundamental computation. Adding two tensors is an operator, as is convolution. Each operator requires a corresponding operator kernel to perform the computation on the target hardware. ExecuTorch backends are the preferred way to do this, but not all operators are supported on all backends.
 
-To handle this, ExecuTorch provides two implementations - the *portable* and *optimized* kernel libraries. The portable kernel library provides full support for all operators in a platform-independent manner. The optimized library carries additional system requirements, but is able to leverage multithreading and vectorized code to achieve greater performance. Operators can be drawn for both for a single build, allowing the optimized library to be used where available with the portable library as a fallback.
+To handle this, ExecuTorch provides two implementations - the *portable* and *optimized* kernel libraries. The portable kernel library provides full support for all operators in a platform-independent manner. The optimized library carries additional system requirements, but is able to leverage multithreading and vectorized code to achieve greater performance. Operators can be drawn from both for a single build, allowing the optimized library to be used where available with the portable library as a fallback.
 
 The choice of kernel library is transparent to the user when using mobile pre-built packages. However, it is important when building from source, especially on embedded systems. On mobile, the optimized operators are preferred where available. See [Overview of ExecuTorch's Kernel Libraries](kernel-library-overview.md) for more information.
 
diff --git a/examples/demo-apps/android/LlamaDemo/README.md b/examples/demo-apps/android/LlamaDemo/README.md
index 8fed04d7ff5..9a6b3b020e7 100644
--- a/examples/demo-apps/android/LlamaDemo/README.md
+++ b/examples/demo-apps/android/LlamaDemo/README.md
@@ -86,7 +86,7 @@ int loadResult = mModule.load();
 
 * `modelCategory`: Indicate whether it’s a text-only or vision model
 * `modePath`: path to the .pte file
-* `tokenizerPath`: path to the tokenizer .bin file
+* `tokenizerPath`: path to the tokenizer file
 * `temperature`: model parameter to adjust the randomness of the model’s output
 
 
diff --git a/examples/demo-apps/android/LlamaDemo/docs/delegates/mediatek_README.md b/examples/demo-apps/android/LlamaDemo/docs/delegates/mediatek_README.md
index 2ad87df0653..4b696cde398 100644
--- a/examples/demo-apps/android/LlamaDemo/docs/delegates/mediatek_README.md
+++ b/examples/demo-apps/android/LlamaDemo/docs/delegates/mediatek_README.md
@@ -24,7 +24,7 @@ In this section, we will need to set up the ExecuTorch repo first with Conda env
 Checkout ExecuTorch repo and sync submodules
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
 ```
 
 Create either a Python virtual environment:
diff --git a/examples/demo-apps/android/LlamaDemo/docs/delegates/qualcomm_README.md b/examples/demo-apps/android/LlamaDemo/docs/delegates/qualcomm_README.md
index 360e92a5f30..51f04f6f8ce 100644
--- a/examples/demo-apps/android/LlamaDemo/docs/delegates/qualcomm_README.md
+++ b/examples/demo-apps/android/LlamaDemo/docs/delegates/qualcomm_README.md
@@ -22,7 +22,7 @@ In this section, we will need to set up the ExecuTorch repo first with Conda env
 Checkout ExecuTorch repo and sync submodules
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
 ```
 
 Create either a Python virtual environment:
@@ -238,4 +238,4 @@ If the app successfully run on your device, you should see something like below:
 </p>
 
 ## Reporting Issues
-If you encountered any bugs or issues following this tutorial please file a bug/issue here on Github.
\ No newline at end of file
+If you encountered any bugs or issues following this tutorial please file a bug/issue here on Github.
diff --git a/examples/demo-apps/android/LlamaDemo/docs/delegates/xnnpack_README.md b/examples/demo-apps/android/LlamaDemo/docs/delegates/xnnpack_README.md
index 3ec0cd5cf49..a9bbc0a9a55 100644
--- a/examples/demo-apps/android/LlamaDemo/docs/delegates/xnnpack_README.md
+++ b/examples/demo-apps/android/LlamaDemo/docs/delegates/xnnpack_README.md
@@ -24,7 +24,7 @@ In this section, we will need to set up the ExecuTorch repo first with Conda env
 Checkout ExecuTorch repo and sync submodules
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
 ```
 
 Create either a Python virtual environment:
diff --git a/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj/project.pbxproj b/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj/project.pbxproj
index 17d7e440185..0c18efe8e71 100644
--- a/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj/project.pbxproj
+++ b/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj/project.pbxproj
@@ -34,8 +34,6 @@
 		03CF43A62CEC5CEC00C7113B /* kernels_custom_debug in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43A52CEC5CEC00C7113B /* kernels_custom_debug */; };
 		03CF43A82CEC5CEC00C7113B /* kernels_optimized in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43A72CEC5CEC00C7113B /* kernels_optimized */; };
 		03CF43AA2CEC5CEC00C7113B /* kernels_optimized_debug in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43A92CEC5CEC00C7113B /* kernels_optimized_debug */; };
-		03CF43AC2CEC5CEC00C7113B /* kernels_portable in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43AB2CEC5CEC00C7113B /* kernels_portable */; };
-		03CF43AE2CEC5CEC00C7113B /* kernels_portable_debug in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43AD2CEC5CEC00C7113B /* kernels_portable_debug */; };
 		03CF43B02CEC5CEC00C7113B /* kernels_quantized in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43AF2CEC5CEC00C7113B /* kernels_quantized */; };
 		03CF43B22CEC5CEC00C7113B /* kernels_quantized_debug in Frameworks */ = {isa = PBXBuildFile; productRef = 03CF43B12CEC5CEC00C7113B /* kernels_quantized_debug */; };
 		03D151B82E0E0908007A38BE /* LLaVARunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 03D151B72E0E0908007A38BE /* LLaVARunner.mm */; };
@@ -46,7 +44,6 @@
 		03D151D02E0E9ACB007A38BE /* text_llm_runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 03D151CD2E0E9ACB007A38BE /* text_llm_runner.cpp */; };
 		03D151D12E0E9ACB007A38BE /* text_decoder_runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 03D151CC2E0E9ACB007A38BE /* text_decoder_runner.cpp */; };
 		03D151D92E0E9E43007A38BE /* ExecuTorchTextLLMRunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 03D151D42E0E9E43007A38BE /* ExecuTorchTextLLMRunner.mm */; };
-		03D151DA2E0E9E43007A38BE /* BUCK in Resources */ = {isa = PBXBuildFile; fileRef = 03D151D72E0E9E43007A38BE /* BUCK */; };
 		03D151DB2E0E9E43007A38BE /* ExecuTorchTextLLMRunner.h in Headers */ = {isa = PBXBuildFile; fileRef = 03D151D32E0E9E43007A38BE /* ExecuTorchTextLLMRunner.h */; };
 		03D151DC2E0E9E43007A38BE /* ExecuTorchLLM.h in Headers */ = {isa = PBXBuildFile; fileRef = 03D151D22E0E9E43007A38BE /* ExecuTorchLLM.h */; };
 		26A6A4282C8A3769005A761E /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26A6A4272C8A3769005A761E /* ImagePicker.swift */; };
@@ -136,7 +133,6 @@
 		03D151D22E0E9E43007A38BE /* ExecuTorchLLM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExecuTorchLLM.h; sourceTree = "<group>"; };
 		03D151D32E0E9E43007A38BE /* ExecuTorchTextLLMRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExecuTorchTextLLMRunner.h; sourceTree = "<group>"; };
 		03D151D42E0E9E43007A38BE /* ExecuTorchTextLLMRunner.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ExecuTorchTextLLMRunner.mm; sourceTree = "<group>"; };
-		03D151D72E0E9E43007A38BE /* BUCK */ = {isa = PBXFileReference; lastKnownFileType = text; path = BUCK; sourceTree = "<group>"; };
 		26A6A4272C8A3769005A761E /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
 		306A71352DC1DC0F00936B1F /* hf_tokenizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hf_tokenizer.h; sourceTree = "<group>"; };
 		306A71362DC1DC0F00936B1F /* pcre2_regex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pcre2_regex.h; sourceTree = "<group>"; };
@@ -191,8 +187,6 @@
 				03CF43A62CEC5CEC00C7113B /* kernels_custom_debug in Frameworks */,
 				03CF43A82CEC5CEC00C7113B /* kernels_optimized in Frameworks */,
 				03CF43AA2CEC5CEC00C7113B /* kernels_optimized_debug in Frameworks */,
-				03CF43AC2CEC5CEC00C7113B /* kernels_portable in Frameworks */,
-				03CF43AE2CEC5CEC00C7113B /* kernels_portable_debug in Frameworks */,
 				03CF43B02CEC5CEC00C7113B /* kernels_quantized in Frameworks */,
 				03CF43B22CEC5CEC00C7113B /* kernels_quantized_debug in Frameworks */,
 			);
@@ -363,11 +357,10 @@
 			isa = PBXGroup;
 			children = (
 				03D151D62E0E9E43007A38BE /* ExecuTorchLLM */,
-				03D151D72E0E9E43007A38BE /* BUCK */,
 			);
 			name = apple;
-			path = /Users/shoumikhin/executorch/extension/llm/apple;
-			sourceTree = "<absolute>";
+			path = ../../../../../../extension/llm/apple;
+			sourceTree = "<group>";
 		};
 		F292B0842D88B0D200BE6839 /* tokenizers */ = {
 			isa = PBXGroup;
@@ -489,8 +482,6 @@
 				03CF43A52CEC5CEC00C7113B /* kernels_custom_debug */,
 				03CF43A72CEC5CEC00C7113B /* kernels_optimized */,
 				03CF43A92CEC5CEC00C7113B /* kernels_optimized_debug */,
-				03CF43AB2CEC5CEC00C7113B /* kernels_portable */,
-				03CF43AD2CEC5CEC00C7113B /* kernels_portable_debug */,
 				03CF43AF2CEC5CEC00C7113B /* kernels_quantized */,
 				03CF43B12CEC5CEC00C7113B /* kernels_quantized_debug */,
 			);
@@ -551,7 +542,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				03D151DA2E0E9E43007A38BE /* BUCK in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -954,7 +944,7 @@
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/pytorch/executorch";
 			requirement = {
-				branch = "swiftpm-0.6.0";
+				branch = "swiftpm-0.7.0";
 				kind = branch;
 			};
 		};
@@ -1016,16 +1006,6 @@
 			package = 03CF43942CEC5CEC00C7113B /* XCRemoteSwiftPackageReference "executorch" */;
 			productName = kernels_optimized_debug;
 		};
-		03CF43AB2CEC5CEC00C7113B /* kernels_portable */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = 03CF43942CEC5CEC00C7113B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_portable;
-		};
-		03CF43AD2CEC5CEC00C7113B /* kernels_portable_debug */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = 03CF43942CEC5CEC00C7113B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_portable_debug;
-		};
 		03CF43AF2CEC5CEC00C7113B /* kernels_quantized */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = 03CF43942CEC5CEC00C7113B /* XCRemoteSwiftPackageReference "executorch" */;
diff --git a/examples/demo-apps/apple_ios/LLaMA/docs/delegates/mps_README.md b/examples/demo-apps/apple_ios/LLaMA/docs/delegates/mps_README.md
index d6bccc0ef47..e5cfb59c62b 100644
--- a/examples/demo-apps/apple_ios/LLaMA/docs/delegates/mps_README.md
+++ b/examples/demo-apps/apple_ios/LLaMA/docs/delegates/mps_README.md
@@ -17,7 +17,7 @@ In this section, we will need to set up the ExecuTorch repo first with Conda env
 Checkout ExecuTorch repo and sync submodules
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
 ```
 
 Create either a Python virtual environment:
diff --git a/examples/demo-apps/apple_ios/LLaMA/docs/delegates/xnnpack_README.md b/examples/demo-apps/apple_ios/LLaMA/docs/delegates/xnnpack_README.md
index de5b3b9ab27..6ad21c48a52 100644
--- a/examples/demo-apps/apple_ios/LLaMA/docs/delegates/xnnpack_README.md
+++ b/examples/demo-apps/apple_ios/LLaMA/docs/delegates/xnnpack_README.md
@@ -16,7 +16,7 @@ In this section, we will need to set up the ExecuTorch repo first with Conda env
 Checkout ExecuTorch repo and sync submodules
 
 ```
-git clone -b viable/strict https://github.com/pytorch/executorch.git && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git && cd executorch
 ```
 
 Create either a Python virtual environment:
diff --git a/examples/demo-apps/react-native/rnllama/ios/LlamaBridge.h b/examples/demo-apps/react-native/rnllama/ios/LlamaBridge.h
index eaac0708e83..5aaf4bc5724 100644
--- a/examples/demo-apps/react-native/rnllama/ios/LlamaBridge.h
+++ b/examples/demo-apps/react-native/rnllama/ios/LlamaBridge.h
@@ -1,9 +1,9 @@
 #ifndef LLaMABridge_h
 #define LLaMABridge_h
 
+#import <LLaMARunner/LLaMARunner.h>
 #import <React/RCTBridgeModule.h>
 #import <React/RCTEventEmitter.h>
-#import "LLaMARunner.h"
 
 NS_ASSUME_NONNULL_BEGIN
 
diff --git a/examples/demo-apps/react-native/rnllama/ios/Release.xcconfig b/examples/demo-apps/react-native/rnllama/ios/Release.xcconfig
deleted file mode 100644
index 6893e1252e7..00000000000
--- a/examples/demo-apps/react-native/rnllama/ios/Release.xcconfig
+++ /dev/null
@@ -1,24 +0,0 @@
-ET_PLATFORM[sdk=iphonesimulator*] = simulator
-ET_PLATFORM[sdk=iphoneos*] = ios
-ET_PLATFORM[sdk=macos*] = macos
-
-// Link the Debug version of ExecuTorch runtime to keep the logs.
-// Switch to Release for better performance if logs are not needed.
-OTHER_LDFLAGS = $(inherited) \
-    -force_load $(BUILT_PRODUCTS_DIR)/libexecutorch_debug_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libbackend_coreml_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libbackend_mps_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libbackend_xnnpack_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libkernels_custom_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libkernels_optimized_$(ET_PLATFORM).a \
-    -force_load $(BUILT_PRODUCTS_DIR)/libkernels_quantized_$(ET_PLATFORM).a \
-    @$(TEMP_DIR)/cmake/linker_flags
-
-// LLaMARunner requires additional dependencies built with CMake in a custom run script phase.
-// Include headers and libraries from $(TEMP_DIR)/cmake for it.
-HEADER_SEARCH_PATHS = $(inherited) \
-    $(SRCROOT)/../../../../../.. \
-    $(TEMP_DIR)/cmake/include
-
-LIBRARY_SEARCH_PATHS = $(inherited) \
-    $(TEMP_DIR)/cmake/lib
diff --git a/examples/demo-apps/react-native/rnllama/ios/rnllama.xcodeproj/project.pbxproj b/examples/demo-apps/react-native/rnllama/ios/rnllama.xcodeproj/project.pbxproj
index 1a56daafaea..68d8ed3e955 100644
--- a/examples/demo-apps/react-native/rnllama/ios/rnllama.xcodeproj/project.pbxproj
+++ b/examples/demo-apps/react-native/rnllama/ios/rnllama.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		036509DE2E1F7CA700C1BC1B /* LLaMARunner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 036509D32E1F7C0800C1BC1B /* LLaMARunner.framework */; };
+		036509DF2E1F7CB100C1BC1B /* LLaMARunner.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 036509D32E1F7C0800C1BC1B /* LLaMARunner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
 		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
 		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
@@ -16,47 +18,30 @@
 		96905EF65AED1B983A6B3ABC /* libPods-rnllama.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-rnllama.a */; };
 		B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
 		BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
-		E931C6482CFAF07E00DA599B /* LlamaRunner.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E931C6412CFAF07E00DA599B /* LlamaRunner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
-		E931C64E2CFAF09400DA599B /* executorch in Frameworks */ = {isa = PBXBuildFile; productRef = E931C64D2CFAF09400DA599B /* executorch */; };
 		E931C67F2CFAF17500DA599B /* LlamaBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = E931C67E2CFAF17500DA599B /* LlamaBridge.mm */; };
-		E931C6822CFAF38500DA599B /* backend_coreml in Frameworks */ = {isa = PBXBuildFile; productRef = E931C6812CFAF38500DA599B /* backend_coreml */; };
-		E931C6842CFAF38A00DA599B /* backend_mps in Frameworks */ = {isa = PBXBuildFile; productRef = E931C6832CFAF38A00DA599B /* backend_mps */; };
-		E931C6862CFAF39500DA599B /* backend_xnnpack in Frameworks */ = {isa = PBXBuildFile; productRef = E931C6852CFAF39500DA599B /* backend_xnnpack */; };
-		E931C6882CFAF39A00DA599B /* kernels_custom in Frameworks */ = {isa = PBXBuildFile; productRef = E931C6872CFAF39A00DA599B /* kernels_custom */; };
-		E931C68A2CFAF3A500DA599B /* kernels_optimized in Frameworks */ = {isa = PBXBuildFile; productRef = E931C6892CFAF3A500DA599B /* kernels_optimized */; };
-		E931C68C2CFAF3AC00DA599B /* kernels_portable in Frameworks */ = {isa = PBXBuildFile; productRef = E931C68B2CFAF3AC00DA599B /* kernels_portable */; };
-		E931C68E2CFAF3B200DA599B /* kernels_quantized in Frameworks */ = {isa = PBXBuildFile; productRef = E931C68D2CFAF3B200DA599B /* kernels_quantized */; };
-		E931C6A72CFBD70E00DA599B /* LLaMARunner.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6A42CFBD70E00DA599B /* LLaMARunner.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		E931C6A82CFBD70E00DA599B /* LLaMARunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = E931C6A52CFBD70E00DA599B /* LLaMARunner.mm */; };
-		E931C6F62CFBD7FF00DA599B /* runner.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6ED2CFBD7FF00DA599B /* runner.h */; };
-		E931C6F72CFBD7FF00DA599B /* llava_runner.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6EE2CFBD7FF00DA599B /* llava_runner.h */; };
-		E931C6F82CFBD7FF00DA599B /* text_prefiller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C6EF2CFBD7FF00DA599B /* text_prefiller.cpp */; };
-		E931C6F92CFBD7FF00DA599B /* text_decoder_runner.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6F02CFBD7FF00DA599B /* text_decoder_runner.h */; };
-		E931C6FA2CFBD7FF00DA599B /* util.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6F12CFBD7FF00DA599B /* util.h */; };
-		E931C6FB2CFBD7FF00DA599B /* llava_runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C6F22CFBD7FF00DA599B /* llava_runner.cpp */; };
-		E931C6FC2CFBD7FF00DA599B /* text_decoder_runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C6F32CFBD7FF00DA599B /* text_decoder_runner.cpp */; };
-		E931C6FD2CFBD7FF00DA599B /* text_prefiller.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6F42CFBD7FF00DA599B /* text_prefiller.h */; };
-		E931C6FE2CFBD7FF00DA599B /* runner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C6F52CFBD7FF00DA599B /* runner.cpp */; };
-		E931C7012CFBD80A00DA599B /* sampler.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C6FF2CFBD80A00DA599B /* sampler.h */; };
-		E931C7022CFBD80A00DA599B /* sampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C7002CFBD80A00DA599B /* sampler.cpp */; };
-		E931C70B2CFBD81E00DA599B /* tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C7032CFBD81E00DA599B /* tokenizer.h */; };
-		E931C70C2CFBD81E00DA599B /* llama_tiktoken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C7042CFBD81E00DA599B /* llama_tiktoken.cpp */; };
-		E931C70D2CFBD81E00DA599B /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C7052CFBD81E00DA599B /* base64.h */; };
-		E931C70E2CFBD81E00DA599B /* bpe_tokenizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C7062CFBD81E00DA599B /* bpe_tokenizer.cpp */; };
-		E931C70F2CFBD81E00DA599B /* bpe_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C7072CFBD81E00DA599B /* bpe_tokenizer.h */; };
-		E931C7102CFBD81E00DA599B /* tiktoken.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C7082CFBD81E00DA599B /* tiktoken.h */; };
-		E931C7112CFBD81E00DA599B /* llama_tiktoken.h in Headers */ = {isa = PBXBuildFile; fileRef = E931C7092CFBD81E00DA599B /* llama_tiktoken.h */; };
-		E931C7122CFBD81E00DA599B /* tiktoken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E931C70A2CFBD81E00DA599B /* tiktoken.cpp */; };
-		E931C7142CFBDED800DA599B /* LlamaRunner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E931C6412CFAF07E00DA599B /* LlamaRunner.framework */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
-		E931C6452CFAF07E00DA599B /* PBXContainerItemProxy */ = {
+		036509D22E1F7C0800C1BC1B /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
-			containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
+			containerPortal = 036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 03729ED52BB1F8DE00152F2E;
+			remoteInfo = LLaMARunner;
+		};
+		036509DC2E1F7C9B00C1BC1B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */;
 			proxyType = 1;
-			remoteGlobalIDString = E931C6402CFAF07E00DA599B;
-			remoteInfo = LlamaRunner;
+			remoteGlobalIDString = 03729ED42BB1F8DE00152F2E;
+			remoteInfo = LLaMARunner;
+		};
+		036509E32E1F983A00C1BC1B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = 036CAF9D2BB1444500D6C2D5;
+			remoteInfo = LLaMA;
 		};
 /* End PBXContainerItemProxy section */
 
@@ -67,7 +52,7 @@
 			dstPath = "";
 			dstSubfolderSpec = 10;
 			files = (
-				E931C6482CFAF07E00DA599B /* LlamaRunner.framework in Embed Frameworks */,
+				036509DF2E1F7CB100C1BC1B /* LLaMARunner.framework in Embed Frameworks */,
 			);
 			name = "Embed Frameworks";
 			runOnlyForDeploymentPostprocessing = 0;
@@ -75,6 +60,7 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = LLaMA.xcodeproj; path = "/Users/shoumikhin/executorch/examples/demo-apps/apple_ios/LLaMA/LLaMA.xcodeproj"; sourceTree = "<absolute>"; };
 		13B07F961A680F5B00A75B9A /* rnllama.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rnllama.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = rnllama/AppDelegate.h; sourceTree = "<group>"; };
 		13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = rnllama/AppDelegate.mm; sourceTree = "<group>"; };
@@ -89,31 +75,8 @@
 		9D82BBB95CF44897A58D7662 /* rnllama-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "rnllama-Bridging-Header.h"; path = "rnllama/rnllama-Bridging-Header.h"; sourceTree = "<group>"; };
 		AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = rnllama/SplashScreen.storyboard; sourceTree = "<group>"; };
 		BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
-		E931C6412CFAF07E00DA599B /* LlamaRunner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = LlamaRunner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		E931C67D2CFAF16000DA599B /* LlamaBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LlamaBridge.h; sourceTree = "<group>"; };
 		E931C67E2CFAF17500DA599B /* LlamaBridge.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LlamaBridge.mm; sourceTree = "<group>"; };
-		E931C6A42CFBD70E00DA599B /* LLaMARunner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLaMARunner.h; sourceTree = "<group>"; };
-		E931C6A52CFBD70E00DA599B /* LLaMARunner.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LLaMARunner.mm; sourceTree = "<group>"; };
-		E931C6ED2CFBD7FF00DA599B /* runner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = runner.h; path = ../../../examples/models/llama/runner/runner.h; sourceTree = "<group>"; };
-		E931C6EE2CFBD7FF00DA599B /* llava_runner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = llava_runner.h; path = ../../../examples/models/llava/runner/llava_runner.h; sourceTree = "<group>"; };
-		E931C6EF2CFBD7FF00DA599B /* text_prefiller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_prefiller.cpp; sourceTree = "<group>"; };
-		E931C6F02CFBD7FF00DA599B /* text_decoder_runner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_decoder_runner.h; sourceTree = "<group>"; };
-		E931C6F12CFBD7FF00DA599B /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = "<group>"; };
-		E931C6F22CFBD7FF00DA599B /* llava_runner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = llava_runner.cpp; path = ../../../examples/models/llava/runner/llava_runner.cpp; sourceTree = "<group>"; };
-		E931C6F32CFBD7FF00DA599B /* text_decoder_runner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = text_decoder_runner.cpp; sourceTree = "<group>"; };
-		E931C6F42CFBD7FF00DA599B /* text_prefiller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_prefiller.h; sourceTree = "<group>"; };
-		E931C6F52CFBD7FF00DA599B /* runner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = runner.cpp; path = ../../../examples/models/llama/runner/runner.cpp; sourceTree = "<group>"; };
-		E931C6FF2CFBD80A00DA599B /* sampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sampler.h; sourceTree = "<group>"; };
-		E931C7002CFBD80A00DA599B /* sampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sampler.cpp; sourceTree = "<group>"; };
-		E931C7032CFBD81E00DA599B /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tokenizer.h; path = ../../../../extension/llm/tokenizer/tokenizer.h; sourceTree = "<group>"; };
-		E931C7042CFBD81E00DA599B /* llama_tiktoken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = llama_tiktoken.cpp; sourceTree = "<group>"; };
-		E931C7052CFBD81E00DA599B /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base64.h; path = ../../../../extension/llm/tokenizer/base64.h; sourceTree = "<group>"; };
-		E931C7062CFBD81E00DA599B /* bpe_tokenizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bpe_tokenizer.cpp; path = ../../../../extension/llm/tokenizer/bpe_tokenizer.cpp; sourceTree = "<group>"; };
-		E931C7072CFBD81E00DA599B /* bpe_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bpe_tokenizer.h; path = ../../../../extension/llm/tokenizer/bpe_tokenizer.h; sourceTree = "<group>"; };
-		E931C7082CFBD81E00DA599B /* tiktoken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tiktoken.h; path = ../../../../extension/llm/tokenizer/tiktoken.h; sourceTree = "<group>"; };
-		E931C7092CFBD81E00DA599B /* llama_tiktoken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = llama_tiktoken.h; sourceTree = "<group>"; };
-		E931C70A2CFBD81E00DA599B /* tiktoken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tiktoken.cpp; path = ../../../../extension/llm/tokenizer/tiktoken.cpp; sourceTree = "<group>"; };
-		E931C7132CFBDABF00DA599B /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
 		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
 		FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-rnllama/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
 /* End PBXFileReference section */
@@ -123,29 +86,23 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				E931C7142CFBDED800DA599B /* LlamaRunner.framework in Frameworks */,
+				036509DE2E1F7CA700C1BC1B /* LLaMARunner.framework in Frameworks */,
 				96905EF65AED1B983A6B3ABC /* libPods-rnllama.a in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		E931C63E2CFAF07E00DA599B /* Frameworks */ = {
-			isa = PBXFrameworksBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				E931C64E2CFAF09400DA599B /* executorch in Frameworks */,
-				E931C68E2CFAF3B200DA599B /* kernels_quantized in Frameworks */,
-				E931C6862CFAF39500DA599B /* backend_xnnpack in Frameworks */,
-				E931C6842CFAF38A00DA599B /* backend_mps in Frameworks */,
-				E931C6822CFAF38500DA599B /* backend_coreml in Frameworks */,
-				E931C6882CFAF39A00DA599B /* kernels_custom in Frameworks */,
-				E931C68C2CFAF3AC00DA599B /* kernels_portable in Frameworks */,
-				E931C68A2CFAF3A500DA599B /* kernels_optimized in Frameworks */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		036509CC2E1F7C0800C1BC1B /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				036509E42E1F983A00C1BC1B /* LLaMA.app */,
+				036509D32E1F7C0800C1BC1B /* LLaMARunner.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		13B07FAE1A68108700A75B9A /* rnllama */ = {
 			isa = PBXGroup;
 			children = (
@@ -184,14 +141,13 @@
 		83CBB9F61A601CBA00E9B192 = {
 			isa = PBXGroup;
 			children = (
-				E931C7132CFBDABF00DA599B /* Release.xcconfig */,
 				13B07FAE1A68108700A75B9A /* rnllama */,
 				832341AE1AAA6A7D00B99B32 /* Libraries */,
-				E931C6422CFAF07E00DA599B /* LlamaRunner */,
 				83CBBA001A601CBA00E9B192 /* Products */,
 				2D16E6871FA4F8E400B85C8A /* Frameworks */,
 				D65327D7A22EEC0BE12398D9 /* Pods */,
 				D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
+				036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */,
 			);
 			indentWidth = 2;
 			sourceTree = "<group>";
@@ -202,7 +158,6 @@
 			isa = PBXGroup;
 			children = (
 				13B07F961A680F5B00A75B9A /* rnllama.app */,
-				E931C6412CFAF07E00DA599B /* LlamaRunner.framework */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -241,102 +196,8 @@
 			name = ExpoModulesProviders;
 			sourceTree = "<group>";
 		};
-		E931C6422CFAF07E00DA599B /* LlamaRunner */ = {
-			isa = PBXGroup;
-			children = (
-				E931C6A12CFBD6D100DA599B /* LlamaRunner */,
-			);
-			path = LlamaRunner;
-			sourceTree = "<group>";
-		};
-		E931C6A12CFBD6D100DA599B /* LlamaRunner */ = {
-			isa = PBXGroup;
-			children = (
-				E931C6E22CFBD77E00DA599B /* tokenizer */,
-				E931C6CE2CFBD77500DA599B /* sampler */,
-				E931C6B72CFBD74400DA599B /* runner */,
-				E931C6A62CFBD70E00DA599B /* Exported */,
-			);
-			path = LlamaRunner;
-			sourceTree = "<group>";
-		};
-		E931C6A62CFBD70E00DA599B /* Exported */ = {
-			isa = PBXGroup;
-			children = (
-				E931C6A42CFBD70E00DA599B /* LLaMARunner.h */,
-				E931C6A52CFBD70E00DA599B /* LLaMARunner.mm */,
-			);
-			name = Exported;
-			path = ../../../../../apple_ios/LLaMA/LLaMARunner/LLaMARunner/Exported;
-			sourceTree = "<group>";
-		};
-		E931C6B72CFBD74400DA599B /* runner */ = {
-			isa = PBXGroup;
-			children = (
-				E931C6F22CFBD7FF00DA599B /* llava_runner.cpp */,
-				E931C6EE2CFBD7FF00DA599B /* llava_runner.h */,
-				E931C6F52CFBD7FF00DA599B /* runner.cpp */,
-				E931C6ED2CFBD7FF00DA599B /* runner.h */,
-				E931C6F32CFBD7FF00DA599B /* text_decoder_runner.cpp */,
-				E931C6F02CFBD7FF00DA599B /* text_decoder_runner.h */,
-				E931C6EF2CFBD7FF00DA599B /* text_prefiller.cpp */,
-				E931C6F42CFBD7FF00DA599B /* text_prefiller.h */,
-				E931C6F12CFBD7FF00DA599B /* util.h */,
-			);
-			name = runner;
-			path = ../../../../../../../extension/llm/runner;
-			sourceTree = "<group>";
-		};
-		E931C6CE2CFBD77500DA599B /* sampler */ = {
-			isa = PBXGroup;
-			children = (
-				E931C7002CFBD80A00DA599B /* sampler.cpp */,
-				E931C6FF2CFBD80A00DA599B /* sampler.h */,
-			);
-			name = sampler;
-			path = ../../../../../../../extension/llm/sampler;
-			sourceTree = "<group>";
-		};
-		E931C6E22CFBD77E00DA599B /* tokenizer */ = {
-			isa = PBXGroup;
-			children = (
-				E931C7052CFBD81E00DA599B /* base64.h */,
-				E931C7062CFBD81E00DA599B /* bpe_tokenizer.cpp */,
-				E931C7072CFBD81E00DA599B /* bpe_tokenizer.h */,
-				E931C7042CFBD81E00DA599B /* llama_tiktoken.cpp */,
-				E931C7092CFBD81E00DA599B /* llama_tiktoken.h */,
-				E931C70A2CFBD81E00DA599B /* tiktoken.cpp */,
-				E931C7082CFBD81E00DA599B /* tiktoken.h */,
-				E931C7032CFBD81E00DA599B /* tokenizer.h */,
-			);
-			name = tokenizer;
-			path = ../../../../../../models/llama/tokenizer;
-			sourceTree = "<group>";
-		};
 /* End PBXGroup section */
 
-/* Begin PBXHeadersBuildPhase section */
-		E931C63C2CFAF07E00DA599B /* Headers */ = {
-			isa = PBXHeadersBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				E931C6FA2CFBD7FF00DA599B /* util.h in Headers */,
-				E931C6F62CFBD7FF00DA599B /* runner.h in Headers */,
-				E931C70D2CFBD81E00DA599B /* base64.h in Headers */,
-				E931C6F72CFBD7FF00DA599B /* llava_runner.h in Headers */,
-				E931C7012CFBD80A00DA599B /* sampler.h in Headers */,
-				E931C70B2CFBD81E00DA599B /* tokenizer.h in Headers */,
-				E931C6FD2CFBD7FF00DA599B /* text_prefiller.h in Headers */,
-				E931C6F92CFBD7FF00DA599B /* text_decoder_runner.h in Headers */,
-				E931C70F2CFBD81E00DA599B /* bpe_tokenizer.h in Headers */,
-				E931C7112CFBD81E00DA599B /* llama_tiktoken.h in Headers */,
-				E931C6A72CFBD70E00DA599B /* LLaMARunner.h in Headers */,
-				E931C7102CFBD81E00DA599B /* tiktoken.h in Headers */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
-/* End PBXHeadersBuildPhase section */
-
 /* Begin PBXNativeTarget section */
 		13B07F861A680F5B00A75B9A /* rnllama */ = {
 			isa = PBXNativeTarget;
@@ -355,42 +216,13 @@
 			buildRules = (
 			);
 			dependencies = (
-				E931C6462CFAF07E00DA599B /* PBXTargetDependency */,
+				036509DD2E1F7C9B00C1BC1B /* PBXTargetDependency */,
 			);
 			name = rnllama;
 			productName = rnllama;
 			productReference = 13B07F961A680F5B00A75B9A /* rnllama.app */;
 			productType = "com.apple.product-type.application";
 		};
-		E931C6402CFAF07E00DA599B /* LlamaRunner */ = {
-			isa = PBXNativeTarget;
-			buildConfigurationList = E931C6492CFAF07E00DA599B /* Build configuration list for PBXNativeTarget "LlamaRunner" */;
-			buildPhases = (
-				E931C6802CFAF1CA00DA599B /* Cmake build */,
-				E931C63C2CFAF07E00DA599B /* Headers */,
-				E931C63D2CFAF07E00DA599B /* Sources */,
-				E931C63E2CFAF07E00DA599B /* Frameworks */,
-				E931C63F2CFAF07E00DA599B /* Resources */,
-			);
-			buildRules = (
-			);
-			dependencies = (
-			);
-			name = LlamaRunner;
-			packageProductDependencies = (
-				E931C64D2CFAF09400DA599B /* executorch */,
-				E931C6812CFAF38500DA599B /* backend_coreml */,
-				E931C6832CFAF38A00DA599B /* backend_mps */,
-				E931C6852CFAF39500DA599B /* backend_xnnpack */,
-				E931C6872CFAF39A00DA599B /* kernels_custom */,
-				E931C6892CFAF3A500DA599B /* kernels_optimized */,
-				E931C68B2CFAF3AC00DA599B /* kernels_portable */,
-				E931C68D2CFAF3B200DA599B /* kernels_quantized */,
-			);
-			productName = LlamaRunner;
-			productReference = E931C6412CFAF07E00DA599B /* LlamaRunner.framework */;
-			productType = "com.apple.product-type.framework";
-		};
 /* End PBXNativeTarget section */
 
 /* Begin PBXProject section */
@@ -403,9 +235,6 @@
 						LastSwiftMigration = 1250;
 						ProvisioningStyle = Automatic;
 					};
-					E931C6402CFAF07E00DA599B = {
-						CreatedOnToolsVersion = 15.3;
-					};
 				};
 			};
 			buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "rnllama" */;
@@ -417,19 +246,38 @@
 				Base,
 			);
 			mainGroup = 83CBB9F61A601CBA00E9B192;
-			packageReferences = (
-				E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */,
-			);
 			productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
 			projectDirPath = "";
+			projectReferences = (
+				{
+					ProductGroup = 036509CC2E1F7C0800C1BC1B /* Products */;
+					ProjectRef = 036509C92E1F7C0800C1BC1B /* LLaMA.xcodeproj */;
+				},
+			);
 			projectRoot = "";
 			targets = (
 				13B07F861A680F5B00A75B9A /* rnllama */,
-				E931C6402CFAF07E00DA599B /* LlamaRunner */,
 			);
 		};
 /* End PBXProject section */
 
+/* Begin PBXReferenceProxy section */
+		036509D32E1F7C0800C1BC1B /* LLaMARunner.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = LLaMARunner.framework;
+			remoteRef = 036509D22E1F7C0800C1BC1B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+		036509E42E1F983A00C1BC1B /* LLaMA.app */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.application;
+			path = LLaMA.app;
+			remoteRef = 036509E32E1F983A00C1BC1B /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
+/* End PBXReferenceProxy section */
+
 /* Begin PBXResourcesBuildPhase section */
 		13B07F8E1A680F5B00A75B9A /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
@@ -442,13 +290,6 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		E931C63F2CFAF07E00DA599B /* Resources */ = {
-			isa = PBXResourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXResourcesBuildPhase section */
 
 /* Begin PBXShellScriptBuildPhase section */
@@ -541,24 +382,6 @@
 			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnllama/Pods-rnllama-resources.sh\"\n";
 			showEnvVarsInLog = 0;
 		};
-		E931C6802CFAF1CA00DA599B /* Cmake build */ = {
-			isa = PBXShellScriptBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-			);
-			inputFileListPaths = (
-			);
-			inputPaths = (
-			);
-			name = "Cmake build";
-			outputFileListPaths = (
-			);
-			outputPaths = (
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-			shellPath = /bin/sh;
-			shellScript = "set -e\n\nif ! command -v cmake &> /dev/null\nthen\n    echo \"cmake not found, please install cmake. \\n1. Download Cmake.app from https://cmake.org/download with version > 3.19. \\n2. Install it to Applications/ folder and run sudo /Applications/CMake.app/Contents/bin/cmake-gui --install to install CMake commandline tools.\"\n    exit 1\nfi\n\nCMAKE_DIR=\"$TEMP_DIR/cmake\"\nrm -rf \"$CMAKE_DIR\"\n\nPLATFORM=\"SIMULATORARM64\"\nDEPLOYMENT_TARGET=\"17.0\"\n\nif [[ \"$PLATFORM_NAME\" == *\"iphoneos\"* ]]; then\n  PLATFORM=\"OS64\"\nelif [[ \"$PLATFORM_NAME\" == *\"macos\"* ]]; then\n  PLATFORM=\"MAC_ARM64\"\n  DEPLOYMENT_TARGET=\"12.0\"\nfi\n\ncmake_build() {\n    local src_dir=$1\n    shift\n    local extra_args=(\"$@\")\n    local build_dir=\"$CMAKE_DIR/build/$(basename \"$src_dir\")\"\n\n    mkdir -p \"$build_dir\" && cd \"$build_dir\"\n    cmake -G Xcode \\\n          -DCMAKE_BUILD_TYPE=\"Release\" \\\n          -DCMAKE_CXX_STANDARD=17 \\\n          -DCMAKE_TOOLCHAIN_FILE=\"$PROJECT_DIR/../../../../../third-party/ios-cmake/ios.toolchain.cmake\" \\\n          -DCMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD=\"c++17\" \\\n          -DCMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY=\"libc++\" \\\n          -DPLATFORM=\"$PLATFORM\" \\\n          -DDEPLOYMENT_TARGET=\"$DEPLOYMENT_TARGET\" \\\n          \"${extra_args[@]}\" \\\n          \"$src_dir\"\n    cmake --build . --config \"Release\"\n    cmake --install . --prefix \"$CMAKE_DIR\"\n}\n\ncmake_build \"$PROJECT_DIR/../../../../../extension/llm/third-party/abseil-cpp\" \\\n    -DABSL_PROPAGATE_CXX_STD=ON\n    \ncmake_build \"$PROJECT_DIR/../../../../../extension/llm/third-party/re2\" \\\n    -DCMAKE_PREFIX_PATH=\"$CMAKE_DIR/lib/cmake/absl\"\n    \ncmake_build \"$PROJECT_DIR/../../../../../extension/llm/third-party/sentencepiece\" \\\n    -DSPM_ENABLE_SHARED=OFF\n\necho \"$(find $CMAKE_DIR/lib -name \"*.a\" | sed -E 's|^.*/lib([^/]+)\\.a|-l\\1|g' | tr '\\n' ' ')\" > \"$CMAKE_DIR/linker_flags\"\n\n\n\n";
-		};
 		F7CCCCE770493310D0125117 /* [Expo] Configure project */ = {
 			isa = PBXShellScriptBuildPhase;
 			alwaysOutOfDate = 1;
@@ -593,29 +416,13 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
-		E931C63D2CFAF07E00DA599B /* Sources */ = {
-			isa = PBXSourcesBuildPhase;
-			buildActionMask = 2147483647;
-			files = (
-				E931C6F82CFBD7FF00DA599B /* text_prefiller.cpp in Sources */,
-				E931C7122CFBD81E00DA599B /* tiktoken.cpp in Sources */,
-				E931C6FB2CFBD7FF00DA599B /* llava_runner.cpp in Sources */,
-				E931C7022CFBD80A00DA599B /* sampler.cpp in Sources */,
-				E931C70E2CFBD81E00DA599B /* bpe_tokenizer.cpp in Sources */,
-				E931C6FE2CFBD7FF00DA599B /* runner.cpp in Sources */,
-				E931C70C2CFBD81E00DA599B /* llama_tiktoken.cpp in Sources */,
-				E931C6A82CFBD70E00DA599B /* LLaMARunner.mm in Sources */,
-				E931C6FC2CFBD7FF00DA599B /* text_decoder_runner.cpp in Sources */,
-			);
-			runOnlyForDeploymentPostprocessing = 0;
-		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
-		E931C6462CFAF07E00DA599B /* PBXTargetDependency */ = {
+		036509DD2E1F7C9B00C1BC1B /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
-			target = E931C6402CFAF07E00DA599B /* LlamaRunner */;
-			targetProxy = E931C6452CFAF07E00DA599B /* PBXContainerItemProxy */;
+			name = LLaMARunner;
+			targetProxy = 036509DC2E1F7C9B00C1BC1B /* PBXContainerItemProxy */;
 		};
 /* End PBXTargetDependency section */
 
@@ -724,6 +531,7 @@
 				COPY_PHASE_STRIP = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = x86_64;
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_DYNAMIC_NO_PIC = NO;
 				GCC_NO_COMMON_BLOCKS = YES;
@@ -746,7 +554,6 @@
 				);
 				LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
 				MTL_ENABLE_DEBUG_INFO = YES;
-				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "$(inherited)  ";
 				REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
 				SDKROOT = iphoneos;
@@ -787,6 +594,7 @@
 				COPY_PHASE_STRIP = YES;
 				ENABLE_NS_ASSERTIONS = NO;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = x86_64;
 				GCC_C_LANGUAGE_STANDARD = gnu99;
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -810,106 +618,6 @@
 			};
 			name = Release;
 		};
-		E931C64A2CFAF07E00DA599B /* Debug */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = E931C7132CFBDABF00DA599B /* Release.xcconfig */;
-			buildSettings = {
-				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "Apple Development";
-				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = dwarf;
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				ENABLE_MODULE_VERIFIER = YES;
-				ENABLE_USER_SCRIPT_SANDBOXING = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu17;
-				GENERATE_INFOPLIST_FILE = YES;
-				INFOPLIST_KEY_NSHumanReadableCopyright = "";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 17.4;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
-				MARKETING_VERSION = 1.0;
-				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
-				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
-				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
-				MTL_FAST_MATH = YES;
-				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
-				PRODUCT_BUNDLE_IDENTIFIER = com.hietalajulius.LlamaRunner;
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
-				SKIP_INSTALL = YES;
-				SWIFT_EMIT_LOC_STRINGS = YES;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Debug;
-		};
-		E931C64B2CFAF07E00DA599B /* Release */ = {
-			isa = XCBuildConfiguration;
-			baseConfigurationReference = E931C7132CFBDABF00DA599B /* Release.xcconfig */;
-			buildSettings = {
-				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
-				CLANG_ANALYZER_NONNULL = YES;
-				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
-				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
-				CLANG_ENABLE_OBJC_WEAK = YES;
-				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
-				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
-				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
-				CODE_SIGN_IDENTITY = "Apple Development";
-				CODE_SIGN_STYLE = Automatic;
-				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 1;
-				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
-				DEFINES_MODULE = YES;
-				DEVELOPMENT_TEAM = "";
-				DYLIB_COMPATIBILITY_VERSION = 1;
-				DYLIB_CURRENT_VERSION = 1;
-				DYLIB_INSTALL_NAME_BASE = "@rpath";
-				ENABLE_MODULE_VERIFIER = YES;
-				ENABLE_USER_SCRIPT_SANDBOXING = YES;
-				GCC_C_LANGUAGE_STANDARD = gnu17;
-				GENERATE_INFOPLIST_FILE = YES;
-				INFOPLIST_KEY_NSHumanReadableCopyright = "";
-				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 17.4;
-				LD_RUNPATH_SEARCH_PATHS = (
-					"$(inherited)",
-					"@executable_path/Frameworks",
-					"@loader_path/Frameworks",
-				);
-				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
-				MARKETING_VERSION = 1.0;
-				MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
-				MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
-				MTL_FAST_MATH = YES;
-				OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
-				PRODUCT_BUNDLE_IDENTIFIER = com.hietalajulius.LlamaRunner;
-				PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
-				SKIP_INSTALL = YES;
-				SWIFT_EMIT_LOC_STRINGS = YES;
-				TARGETED_DEVICE_FAMILY = "1,2";
-				VERSIONING_SYSTEM = "apple-generic";
-				VERSION_INFO_PREFIX = "";
-			};
-			name = Release;
-		};
 /* End XCBuildConfiguration section */
 
 /* Begin XCConfigurationList section */
@@ -931,70 +639,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		E931C6492CFAF07E00DA599B /* Build configuration list for PBXNativeTarget "LlamaRunner" */ = {
-			isa = XCConfigurationList;
-			buildConfigurations = (
-				E931C64A2CFAF07E00DA599B /* Debug */,
-				E931C64B2CFAF07E00DA599B /* Release */,
-			);
-			defaultConfigurationIsVisible = 0;
-			defaultConfigurationName = Release;
-		};
 /* End XCConfigurationList section */
-
-/* Begin XCRemoteSwiftPackageReference section */
-		E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */ = {
-			isa = XCRemoteSwiftPackageReference;
-			repositoryURL = "https://github.com/pytorch/executorch.git";
-			requirement = {
-				branch = "swiftpm-0.7.0.20250401";
-				kind = branch;
-			};
-		};
-/* End XCRemoteSwiftPackageReference section */
-
-/* Begin XCSwiftPackageProductDependency section */
-		E931C64D2CFAF09400DA599B /* executorch */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = executorch;
-		};
-		E931C6812CFAF38500DA599B /* backend_coreml */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = backend_coreml;
-		};
-		E931C6832CFAF38A00DA599B /* backend_mps */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = backend_mps;
-		};
-		E931C6852CFAF39500DA599B /* backend_xnnpack */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = backend_xnnpack;
-		};
-		E931C6872CFAF39A00DA599B /* kernels_custom */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_custom;
-		};
-		E931C6892CFAF3A500DA599B /* kernels_optimized */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_optimized;
-		};
-		E931C68B2CFAF3AC00DA599B /* kernels_portable */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_portable;
-		};
-		E931C68D2CFAF3B200DA599B /* kernels_quantized */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = E931C62F2CFAF06100DA599B /* XCRemoteSwiftPackageReference "executorch" */;
-			productName = kernels_quantized;
-		};
-/* End XCSwiftPackageProductDependency section */
 	};
 	rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
 }
diff --git a/examples/models/llama/README.md b/examples/models/llama/README.md
index bbd2107ad74..9721521916b 100644
--- a/examples/models/llama/README.md
+++ b/examples/models/llama/README.md
@@ -65,7 +65,7 @@ Please see the [Llama 3.2 model card](https://github.com/meta-llama/llama-models
 
 ### Performance
 
-Llama 3.2 1B and 3B performance was measured on Android OnePlus 12 device. The performance measurement is expressed in terms of tokens per second using an [adb binary-based approach](#step-4-run-benchmark-on-android-phone) with prompt length of 64. It is measured with KleidiAI library. KleidiAI is not enabled by default yet. Use `-DEXECUTORCH_XNNPACK_ENABLE_KLEIDI=ON` to enable it in the build.
+Llama 3.2 1B and 3B performance was measured on Android OnePlus 12 device. The performance measurement is expressed in terms of tokens per second using an [adb binary-based approach](#step-4-run-benchmark-on-android-phone) with prompt length of 64. It is measured with KleidiAI library. KleidiAI is enabled by default on the XNNPACK Backend for all ARM devices.
 
 |Model  | Decode (tokens/s) | Time-to-first-token (sec) | Prefill (tokens/s) | Model size (PTE file size in MiB) | Memory size (RSS in MiB) |
 |-------|------------------:|--------------------------:| ------------------:|----------------------------------:| ------------------------:|
@@ -338,7 +338,12 @@ Please refer to [this tutorial](https://pytorch.org/executorch/main/llm/llama-de
 
 ## Running with low-bit kernels
 
-We now give instructions for quantizating and running your model with low-bit kernels.  These are still experimental, and require you do development on an Arm-based Mac.  Also note that low-bit quantization often requires QAT (quantization-aware training) to give good quality results.
+We now give instructions for quantizating and running your model with low-bit kernels.  These are still experimental, and require you do development on an Arm-based Mac, and install executorch from source with the environment variable EXECUTORCH_BUILD_TORCHAO=1 defined:
+```
+EXECUTORCH_BUILD_TORCHAO=1 python install_executorch.py
+```
+
+Also note that low-bit quantization often requires QAT (quantization-aware training) to give good quality results.
 
 First export your model for lowbit quantization (step 2 above):
 
diff --git a/examples/selective_build/README.md b/examples/selective_build/README.md
index 97706d70c48..888020ce770 100644
--- a/examples/selective_build/README.md
+++ b/examples/selective_build/README.md
@@ -12,12 +12,12 @@ cd executorch
 bash examples/selective_build/test_selective_build.sh cmake
 ```
 
-Check out `CMakeLists.txt` for demo of 3 selective build APIs:
+Check out `CMakeLists.txt` for demo of selective build APIs:
 1. `SELECT_ALL_OPS`: Select all ops from the dependency kernel libraries, register all of them into ExecuTorch runtime.
 2. `SELECT_OPS_LIST`: Only select operators from a list.
 3. `SELECT_OPS_YAML`: Only select operators from a yaml file.
+4. `SELECT_OPS_FROM_MODEL`: Only select operators from a from an exported model pte.
+5. `DTYPE_SELECTIVE_BUILD`: Enable rebuild of `portable_kernels` to use dtype selection. Currently only supported for `SELECTED_OPS_FROM_MODEL` API and `portable_kernels` lib.
 
 Other configs:
 - `MAX_KERNEL_NUM=N`: Only allocate memory for N operators.
-
-We have one more API incoming: only select from an exported model file (.pte).
diff --git a/extension/android/CMakeLists.txt b/extension/android/CMakeLists.txt
index 8f7e19cb172..acd419cfc31 100644
--- a/extension/android/CMakeLists.txt
+++ b/extension/android/CMakeLists.txt
@@ -114,6 +114,9 @@ endif()
 if(TARGET xnnpack_backend)
   target_link_options_shared_lib(xnnpack_backend)
   list(APPEND link_libraries xnnpack_backend XNNPACK pthreadpool cpuinfo xnnpack-microkernels-prod)
+  if(TARGET kleidiai)
+    list(APPEND link_libraries kleidiai)
+  endif()
 endif()
 
 if(TARGET vulkan_backend)
diff --git a/extension/android/build.gradle b/extension/android/build.gradle
index ac031653a7a..3a5d42e9838 100644
--- a/extension/android/build.gradle
+++ b/extension/android/build.gradle
@@ -17,7 +17,7 @@ allprojects {
 
         dependencies {
             classpath 'com.android.tools.build:gradle:8.9.0'
-            classpath 'com.vanniktech:gradle-maven-publish-plugin:0.31.0'
+            classpath 'com.vanniktech:gradle-maven-publish-plugin:0.34.0'
         }
 
     }
diff --git a/extension/android/executorch_android/build.gradle b/extension/android/executorch_android/build.gradle
index 2fa0b9fd57c..ec37b293024 100644
--- a/extension/android/executorch_android/build.gradle
+++ b/extension/android/executorch_android/build.gradle
@@ -56,10 +56,8 @@ dependencies {
     androidTestImplementation 'org.json:json:20250107'
 }
 
-import com.vanniktech.maven.publish.SonatypeHost
-
 mavenPublishing {
-  publishToMavenCentral(SonatypeHost.DEFAULT)
+  publishToMavenCentral()
   signAllPublications()
 
   coordinates("org.pytorch", "executorch-android", "0.7.0")
@@ -94,6 +92,6 @@ mavenPublishing {
 
 repositories {
     maven {
-        url "https://oss.sonatype.org/content/repositories/snapshots"
+        url "https://central.sonatype.com/repository/maven-snapshots/"
     }
 }
diff --git a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/annotations/package-info.java b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/annotations/package-info.java
new file mode 100644
index 00000000000..2173a04c69d
--- /dev/null
+++ b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/annotations/package-info.java
@@ -0,0 +1,2 @@
+/** Annotations used by ExecuTorch Android Java/JNI package. */
+package org.pytorch.executorch.annotations;
diff --git a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/extension/llm/package-info.java b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/extension/llm/package-info.java
new file mode 100644
index 00000000000..2fcc8c9ec6b
--- /dev/null
+++ b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/extension/llm/package-info.java
@@ -0,0 +1,2 @@
+/** Extension for LLM related use cases for ExecuTorch Android Java/JNI package. */
+package org.pytorch.executorch.extension.llm;
diff --git a/extension/android/executorch_android/src/main/java/org/pytorch/executorch/package-info.java b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/package-info.java
new file mode 100644
index 00000000000..01d55ebc72b
--- /dev/null
+++ b/extension/android/executorch_android/src/main/java/org/pytorch/executorch/package-info.java
@@ -0,0 +1,2 @@
+/** ExecuTorch Android Java/JNI package. This is the main package for generic use cases. */
+package org.pytorch.executorch;
diff --git a/extension/apple/ExecuTorch/Exported/ExecuTorchError.h b/extension/apple/ExecuTorch/Exported/ExecuTorchError.h
index 6a8ab7bc2fc..e53908687b0 100644
--- a/extension/apple/ExecuTorch/Exported/ExecuTorchError.h
+++ b/extension/apple/ExecuTorch/Exported/ExecuTorchError.h
@@ -19,30 +19,34 @@ FOUNDATION_EXPORT NSErrorDomain const ExecuTorchErrorDomain NS_SWIFT_NAME(ErrorD
  */
 typedef NS_ERROR_ENUM(ExecuTorchErrorDomain, ExecuTorchErrorCode) {
   // System errors.
-  ExecuTorchErrorCodeOk                             = 0,
-  ExecuTorchErrorCodeInternal                       = 1,
-  ExecuTorchErrorCodeInvalidState                   = 2,
-  ExecuTorchErrorCodeEndOfMethod                    = 3,
+  ExecuTorchErrorCodeOk                              = 0,
+  ExecuTorchErrorCodeInternal                        = 1,
+  ExecuTorchErrorCodeInvalidState                    = 2,
+  ExecuTorchErrorCodeEndOfMethod                     = 3,
 
   // Logical errors.
-  ExecuTorchErrorCodeNotSupported                   = 16,
-  ExecuTorchErrorCodeNotImplemented                 = 17,
-  ExecuTorchErrorCodeInvalidArgument                = 18,
-  ExecuTorchErrorCodeInvalidType                    = 19,
-  ExecuTorchErrorCodeOperatorMissing                = 20,
+  ExecuTorchErrorCodeNotSupported                    = 16,
+  ExecuTorchErrorCodeNotImplemented                  = 17,
+  ExecuTorchErrorCodeInvalidArgument                 = 18,
+  ExecuTorchErrorCodeInvalidType                     = 19,
+  ExecuTorchErrorCodeOperatorMissing                 = 20,
+
+  // Registration errors.
+  ExecuTorchErrorCodeRegistrationExceedingMaxKernels = 21,
+  ExecuTorchErrorCodeRegistrationAlreadyRegistered   = 22,
 
   // Resource errors.
-  ExecuTorchErrorCodeNotFound                       = 32,
-  ExecuTorchErrorCodeMemoryAllocationFailed         = 33,
-  ExecuTorchErrorCodeAccessFailed                   = 34,
-  ExecuTorchErrorCodeInvalidProgram                 = 35,
-  ExecuTorchErrorCodeInvalidExternalData            = 36,
-  ExecuTorchErrorCodeOutOfResources                 = 37,
+  ExecuTorchErrorCodeNotFound                        = 32,
+  ExecuTorchErrorCodeMemoryAllocationFailed          = 33,
+  ExecuTorchErrorCodeAccessFailed                    = 34,
+  ExecuTorchErrorCodeInvalidProgram                  = 35,
+  ExecuTorchErrorCodeInvalidExternalData             = 36,
+  ExecuTorchErrorCodeOutOfResources                  = 37,
 
   // Delegate errors.
-  ExecuTorchErrorCodeDelegateInvalidCompatibility   = 48,
-  ExecuTorchErrorCodeDelegateMemoryAllocationFailed = 49,
-  ExecuTorchErrorCodeDelegateInvalidHandle          = 50,
+  ExecuTorchErrorCodeDelegateInvalidCompatibility    = 48,
+  ExecuTorchErrorCodeDelegateMemoryAllocationFailed  = 49,
+  ExecuTorchErrorCodeDelegateInvalidHandle           = 50,
 } NS_SWIFT_NAME(ErrorCode);
 
 /**
diff --git a/extension/apple/ExecuTorch/Exported/ExecuTorchError.m b/extension/apple/ExecuTorch/Exported/ExecuTorchError.m
index 20b3af2e349..26929554bf7 100644
--- a/extension/apple/ExecuTorch/Exported/ExecuTorchError.m
+++ b/extension/apple/ExecuTorch/Exported/ExecuTorchError.m
@@ -30,6 +30,10 @@
       return @"Invalid type";
     case ExecuTorchErrorCodeOperatorMissing:
       return @"Operator missing";
+    case ExecuTorchErrorCodeRegistrationExceedingMaxKernels:
+      return @"Exceeded maximum number of kernels";
+    case ExecuTorchErrorCodeRegistrationAlreadyRegistered:
+      return @"Kernel is already registered";
     case ExecuTorchErrorCodeNotFound:
       return @"Resource not found";
     case ExecuTorchErrorCodeMemoryAllocationFailed:
diff --git a/extension/benchmark/apple/Benchmark/README.md b/extension/benchmark/apple/Benchmark/README.md
index 25e78a39462..e9850376863 100644
--- a/extension/benchmark/apple/Benchmark/README.md
+++ b/extension/benchmark/apple/Benchmark/README.md
@@ -24,7 +24,7 @@ It provides a flexible framework for dynamically generating and running performa
 To get started, clone the ExecuTorch repository and cd into the source code directory:
 
 ```bash
-git clone -b viable/strict https://github.com/pytorch/executorch.git --depth 1 --recurse-submodules --shallow-submodules && cd executorch
+git clone -b release/0.7 https://github.com/pytorch/executorch.git --depth 1 --recurse-submodules --shallow-submodules && cd executorch
 ```
 
 This command performs a shallow clone to speed up the process.
diff --git a/extension/flat_tensor/README.md b/extension/flat_tensor/README.md
index 7ece0eb707a..b1d8ed8a8fc 100644
--- a/extension/flat_tensor/README.md
+++ b/extension/flat_tensor/README.md
@@ -1,6 +1,59 @@
 ## FlatTensor
 
-> [!IMPORTANT]
-> FlatTensor is still under development, and not ready to use.
+FlatTensor is a flatbuffer-based format for storing and loading data with string-based keys. The format provides efficient serialization and deserialization of data with metadata and supports C++ and Python APIs. FlatTensor files use the `.ptd` extension.
 
-FlatTensor is a flatbuffer-based format for storing and loading tensors. The format provides a way to store tensors keyed by string.
+Major usage is to store data outside of the PTE file for clean program-data separation. Stored data may be tensor data or opaque blob data (for backends that do not expose data format).
+
+### Schema
+
+[flat_tensor.fbs](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/flat_tensor.fbs) contains the [Flatbuffers](https://google.github.io/flatbuffers/) schema used to serialize ExecuTorch data files.
+
+[flat_tensor_schema.py](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/flat_tensor_schema.py) contains the python definition of the schema types.
+
+### C++ APIs
+
+[serialize.h](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/serialize.h) contains the APIs to serialize a PTD file.
+
+[flat_tensor_data_map.h](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/flat_tensor_data_map.h) contains the APIs to deserialize a PTD file and interact with it via the [named_data_map.h](https://github.com/pytorch/executorch/blob/main/runtime/core/named_data_map.h) interface.
+
+### Python APIs
+
+[serialize.py](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/serialize.py) contains the Python serialization and deserialization APIs.
+
+### Alignment Considerations
+
+**Segment alignment**: Data segments are aligned to this value. This is usually some multiple of 2. Specified in the [FlatTensorConfig](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/serialize.py#L96).
+
+**Tensor alignment**: Tensors are aligned to this value. Specified in the [FlatTensorConfig](https://github.com/pytorch/executorch/blob/main/extension/flat_tensor/serialize/serialize.py#L96).
+
+**Blob alignment**: Blobs (may not be canonical tensors) are aligned to this value. Alignment is specified when blobs are added to the [_named_data_store.py](https://github.com/pytorch/executorch/blob/main/exir/_serialize/_named_data_store.py#L48) and passed to serialize.py.
+
+FlatTensor does not store alignment in the serialized file; the user must ensure the serialized and runtime-expected alignment correspond. The final alignment may be a larger multiple of the specified alignment, as multiple `NamedData` entries can point to a single `DataSegment`. For example:
+```
+BackendA: {key = key1, data = 0x100, alignment = 4}
+BackendB: {key = key2, data = 0x100, alignment = 8}
+```
+BackendA and BackendB are serializing the same bytes, so the data is deduplicated and the final alignment is the lcm of the two, in this case 8.
+
+### Usage
+
+**AoT**
+
+To export a model as a PTE and PTD pair, see [export_program.py](https://github.com/pytorch/executorch/blob/main/test/models/export_program.py). Use the `--external-constants` argument to move all constants to the separate PTD file.
+```
+python -m test.models.export_program --modules "ModuleAddMul" --external-constants --outdir .
+```
+
+To export a delegated model as PTE and PTD pair, see [export_delegated_program.py](https://github.com/pytorch/executorch/blob/main/test/models/export_delegated_program.py). Use the `--external-constants` argument to move all constants to the separate PTD file. Note, ModuleLinear is used here as linear is consumed by the XNNPACK backend.
+```
+python -m test.models.export_delegated_program --modules ModuleLinear --backend_id XnnpackBackend --external_constants --outdir .
+```
+
+**Runtime**
+
+The `ProgramDataSeparationTest` in [method_test.cpp](https://github.com/pytorch/executorch/blob/main/runtime/executor/test/method_test.cpp) demonstrates how to consume the PTD file at runtime.
+
+For a backend example with XNNPACK, see [test_xnn_data_separation.cpp](https://github.com/pytorch/executorch/blob/main/backends/xnnpack/test/runtime/test_xnn_data_separation.cpp).
+
+### Rules to ensure forward/backward compatibility
+See [executorch/schema/README.md](https://github.com/pytorch/executorch/blob/main/schema/README.md).
diff --git a/extension/llm/runner/text_decoder_runner.cpp b/extension/llm/runner/text_decoder_runner.cpp
index e60a07bc50a..4293b2a08d8 100644
--- a/extension/llm/runner/text_decoder_runner.cpp
+++ b/extension/llm/runner/text_decoder_runner.cpp
@@ -52,22 +52,25 @@ ::executorch::runtime::Result<executorch::aten::Tensor> TextDecoderRunner::step(
     auto numel = sizes[0];
     std::vector<::executorch::aten::SizesType> sizes_vec = {numel};
 
-    // Assuming the last dimension is the one with the variable token length,
-    // for example [1, S] or [1, 1, S]
-    sizes_vec[sizes_vec.size() - 1] = numel;
     TensorPtr start_pos_tensor;
     if (numel > 1) {
-      // Assuming model is exported with cache_positions, create a tensor with
-      // the same size as cache_positions
+      // If we are here, model is exported with cache_positions, create a tensor
+      // with the same length as input_ids. Assuming the last dimension is the
+      // one with the variable token length, for example [1, S] or [1, 1, S]
+      sizes_vec[sizes_vec.size() - 1] = tokens->numel();
       start_pos_tensor = empty(sizes_vec, ::executorch::aten::ScalarType::Long);
       torch::executor::native::arange_out_impl(
-          start_pos, start_pos + numel, 1.0, *start_pos_tensor);
+          start_pos, start_pos + tokens->numel(), 1.0, *start_pos_tensor);
     } else {
       // Assuming model is exported with input_pos, create a tensor with size 1
       start_pos_tensor = from_blob(
           &start_pos, sizes_vec, ::executorch::aten::ScalarType::Long);
     }
-    ET_LOG(Info, "Start pos tensor numel: %zu", start_pos_tensor->numel());
+    ET_LOG(
+        Info,
+        "Start pos tensor numel: %zu, tokens numel: %zu",
+        start_pos_tensor->numel(),
+        tokens->numel());
     auto outputs_res = module_->forward({tokens, start_pos_tensor});
     ET_CHECK_OK_OR_RETURN_ERROR(outputs_res.error());
     ET_CHECK_MSG(
diff --git a/extension/llm/runner/text_prefiller.h b/extension/llm/runner/text_prefiller.h
index ce12506a05c..a02cd3d1bf4 100644
--- a/extension/llm/runner/text_prefiller.h
+++ b/extension/llm/runner/text_prefiller.h
@@ -21,7 +21,7 @@ class ET_EXPERIMENTAL TextPrefiller {
  public:
   TextPrefiller(
       TextDecoderRunner* text_decoder_runner,
-      bool use_kv_cache_,
+      bool use_kv_cache,
       bool enable_parallel_prefill,
       int64_t max_seq_len = 128);
 
diff --git a/install_executorch.py b/install_executorch.py
index 22a9b8f3725..a6cb89dd587 100644
--- a/install_executorch.py
+++ b/install_executorch.py
@@ -50,6 +50,10 @@ def clean():
     print("Cleaning buck-out/...")
     shutil.rmtree("buck-out/", ignore_errors=True)
 
+    # Removes all buck cached state and metadata
+    print("Cleaning buck cached state and metadata ...")
+    shutil.rmtree(os.path.expanduser("~/.buck/buckd"), ignore_errors=True)
+
     # Clean ccache if available
     try:
         result = subprocess.run(["ccache", "--version"], capture_output=True, text=True)
diff --git a/install_requirements.py b/install_requirements.py
index cfece24b1c2..0d44956c47d 100644
--- a/install_requirements.py
+++ b/install_requirements.py
@@ -58,20 +58,8 @@ def python_is_compatible():
     return True
 
 
-# The pip repository that hosts nightly torch packages.
-TORCH_NIGHTLY_URL = "https://download.pytorch.org/whl/nightly/cpu"
-
-
-# Since ExecuTorch often uses main-branch features of pytorch, only the nightly
-# pip versions will have the required features.
-#
-# NOTE: If a newly-fetched version of the executorch repo changes the value of
-# NIGHTLY_VERSION, you should re-run this script to install the necessary
-# package versions.
-#
-# NOTE: If you're changing, make the corresponding change in .ci/docker/ci_commit_pins/pytorch.txt
-# by picking the hash from the same date in https://hud.pytorch.org/hud/pytorch/pytorch/nightly/
-NIGHTLY_VERSION = "dev20250625"
+# The pip repository that hosts torch packages.
+TORCH_URL = "https://download.pytorch.org/whl/test/cpu"
 
 
 def install_requirements(use_pytorch_nightly):
@@ -89,7 +77,7 @@ def install_requirements(use_pytorch_nightly):
         # Setting use_pytorch_nightly to false to test the pinned PyTorch commit. Note
         # that we don't need to set any version number there because they have already
         # been installed on CI before this step, so pip won't reinstall them
-        f"torch==2.8.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torch",
+        "torch==2.8.0" if use_pytorch_nightly else "torch",
     ]
 
     # Install the requirements for core ExecuTorch package.
@@ -105,7 +93,7 @@ def install_requirements(use_pytorch_nightly):
             "requirements-dev.txt",
             *TORCH_PACKAGE,
             "--extra-index-url",
-            TORCH_NIGHTLY_URL,
+            TORCH_URL,
         ],
         check=True,
     )
@@ -118,7 +106,14 @@ def install_requirements(use_pytorch_nightly):
     # Install packages directly from local copy instead of pypi.
     # This is usually not recommended.
     new_env = os.environ.copy()
-    new_env["USE_CPP"] = "1"  # install torchao kernels
+    if ("EXECUTORCH_BUILD_TORCHAO" not in new_env) or (
+        new_env["EXECUTORCH_BUILD_TORCHAO"] == "0"
+    ):
+        new_env["USE_CPP"] = "0"
+    else:
+        assert new_env["EXECUTORCH_BUILD_TORCHAO"] == "1"
+        new_env["USE_CPP"] = "1"
+        new_env["CMAKE_POLICY_VERSION_MINIMUM"] = "3.5"
     subprocess.run(
         [
             sys.executable,
@@ -144,29 +139,8 @@ def install_optional_example_requirements(use_pytorch_nightly):
             "install",
             "-r",
             "requirements-examples.txt",
-        ],
-        check=True,
-    )
-
-    print("Installing torch domain libraries")
-    DOMAIN_LIBRARIES = [
-        (
-            f"torchvision==0.23.0.{NIGHTLY_VERSION}"
-            if use_pytorch_nightly
-            else "torchvision"
-        ),
-        f"torchaudio==2.8.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torchaudio",
-    ]
-    # Then install domain libraries
-    subprocess.run(
-        [
-            sys.executable,
-            "-m",
-            "pip",
-            "install",
-            *DOMAIN_LIBRARIES,
             "--extra-index-url",
-            TORCH_NIGHTLY_URL,
+            TORCH_URL,
         ],
         check=True,
     )
diff --git a/kernels/portable/cpu/util/elementwise_util.h b/kernels/portable/cpu/util/elementwise_util.h
index 0a2d4bfc89a..6adf81f70e3 100644
--- a/kernels/portable/cpu/util/elementwise_util.h
+++ b/kernels/portable/cpu/util/elementwise_util.h
@@ -10,7 +10,6 @@
 
 #include <c10/util/irange.h>
 #include <executorch/kernels/portable/cpu/scalar_utils.h>
-#include <executorch/kernels/portable/cpu/selective_build.h>
 #include <executorch/kernels/portable/cpu/util/broadcast_indexes_range.h>
 #include <executorch/kernels/portable/cpu/util/broadcast_util.h>
 #include <executorch/kernels/portable/cpu/util/dtype_util.h>
@@ -319,22 +318,20 @@ inline void apply_elementwise_fn(
   }
 
   constexpr auto compute_type = CppTypeToScalarType<CTYPE_COMPUTE>::value;
-  if constexpr (should_include_kernel_dtype(op_name, compute_type)) {
-    const bool all_inputs_compute_dtype =
-        ((inputs.first->scalar_type() == compute_type) && ...);
-
-    constexpr ScalarType out_specialized_scalar_type =
-        specialized_output_scalar_type<CTYPE_COMPUTE>(out_dtypes);
-    if (all_inputs_compute_dtype &&
-        out.scalar_type() == out_specialized_scalar_type) {
-      using CTYPE_OUT =
-          typename ScalarTypeToCppType<out_specialized_scalar_type>::type;
-      dtype_specialized_elementwise_fn_impl<
-          CTYPE_COMPUTE,
-          CTYPE_OUT,
-          support_noncontiguous_tensors>(compute_fun, ctx, out, inputs...);
-      return;
-    }
+  const bool all_inputs_compute_dtype =
+      ((inputs.first->scalar_type() == compute_type) && ...);
+
+  constexpr ScalarType out_specialized_scalar_type =
+      specialized_output_scalar_type<CTYPE_COMPUTE>(out_dtypes);
+  if (all_inputs_compute_dtype &&
+      out.scalar_type() == out_specialized_scalar_type) {
+    using CTYPE_OUT =
+        typename ScalarTypeToCppType<out_specialized_scalar_type>::type;
+    dtype_specialized_elementwise_fn_impl<
+        CTYPE_COMPUTE,
+        CTYPE_OUT,
+        support_noncontiguous_tensors>(compute_fun, ctx, out, inputs...);
+    return;
   }
 
   apply_elementwise_fn_generic_impl<
diff --git a/kernels/portable/cpu/util/targets.bzl b/kernels/portable/cpu/util/targets.bzl
index 1806ebb0d5a..d158ab136ab 100644
--- a/kernels/portable/cpu/util/targets.bzl
+++ b/kernels/portable/cpu/util/targets.bzl
@@ -115,11 +115,11 @@ def define_common_targets():
             ":vectorized_math",
             "//executorch/runtime/core/portable_type/c10/c10:aten_headers_for_executorch",
             "//executorch/runtime/kernel:kernel_runtime_context",
-            "//executorch/kernels/portable/cpu:scalar_utils",
             "//executorch/extension/threadpool:threadpool",
             "//executorch/kernels/portable/cpu:scalar_utils",
         ],
         deps = [
+            "//executorch/kernels/portable/cpu:scalar_utils",
             "//executorch/runtime/kernel:kernel_includes",
         ],
         visibility = ["//executorch/kernels/portable/cpu/...", "//executorch/kernels/optimized/cpu/...", "@EXECUTORCH_CLIENTS"],
diff --git a/kernels/prim_ops/et_copy_index.cpp b/kernels/prim_ops/et_copy_index.cpp
index e3d9ae46e54..2b3f55bf5a0 100644
--- a/kernels/prim_ops/et_copy_index.cpp
+++ b/kernels/prim_ops/et_copy_index.cpp
@@ -86,11 +86,9 @@ void et_copy_index(KernelRuntimeContext& context, EValue** stack) {
     // If we're copying past the first index then the shape of
     // copy_from and copy_to without the leading dimension should be
     // the same. i.e. copy_to.size[1:] == copy_from.size[:].
-    if (index > 0) {
-      ET_CHECK_MSG(
-          copy_to.sizes()[i + 1] == copy_from.sizes()[i],
-          "Mismatch in shape between copy_to and copy_from tensors");
-    }
+    ET_CHECK_MSG(
+        copy_to.sizes()[i + 1] == copy_from.sizes()[i],
+        "Mismatch in shape between copy_to and copy_from tensors");
     expected_output_size[i + 1] = copy_from.sizes()[i];
   }
 
@@ -111,8 +109,17 @@ void et_copy_index(KernelRuntimeContext& context, EValue** stack) {
   // If we've reached here, it means the copy_to tensor has been
   // successfully resized so we can now copy over the data from
   // copy_from into the copy_to tensor.
+
+  // Check that the destination has enough space for the copy.
+  size_t offset = index * size_copy_from;
+  size_t copy_to_size = copy_to.element_size() * copy_to.numel();
+  ET_CHECK_MSG(
+      offset + size_copy_from <= copy_to_size,
+      "Buffer overflow: copy_to tensor is smaller than copy_from tensor.");
+
   memcpy(
-      (void*)((uintptr_t)copy_to_ptr + index * size_copy_from),
+      // NOLINTNEXTLINE(performance-no-int-to-ptr)
+      (void*)((uintptr_t)copy_to_ptr + offset),
       copy_from_ptr,
       size_copy_from);
 }
diff --git a/kernels/prim_ops/test/prim_ops_test.cpp b/kernels/prim_ops/test/prim_ops_test.cpp
index f0131cb6a18..7e517a4d377 100644
--- a/kernels/prim_ops/test/prim_ops_test.cpp
+++ b/kernels/prim_ops/test/prim_ops_test.cpp
@@ -131,7 +131,7 @@ TEST_F(RegisterPrimOpsTest, TestETCopyIndex) {
   Tensor copy_to = tf.make({2, 2}, {0, 0, 0, 0});
 #else
   std::vector<int> buf(4);
-  SizesType expected_output_size[2] = {0, 0};
+  SizesType expected_output_size[2] = {0, 2};
   Tensor copy_to =
       tf.make({2, 2}, {0, 0, 0, 0}, {}, TensorShapeDynamism::DYNAMIC_BOUND);
   // Resize the tensor to 0 size for the tests.
diff --git a/pyproject.toml b/pyproject.toml
index 686c9ef3b4c..68d742a2795 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [build-system]
 requires = [
-  "cmake<4.0.0",  # For building binary targets in the wheel. 4.0.0 breaks third-party CMake build so temporarily pin the version.
+  "cmake>=3.29,<4.0.0",  # For building binary targets in the wheel. 4.0.0 breaks third-party CMake build so temporarily pin the version.
   "pip>=23",  # For building the pip package.
   "pyyaml",  # Imported by the kernel codegen tools.
   "setuptools>=63",  # For building the pip package contents.
@@ -67,6 +67,8 @@ dependencies=[
   "pyyaml",
   "ruamel.yaml",
   "sympy",
+  "torch>=2.8.0,<2.9.0",
+  "torchao==0.12.0",
   "tabulate",
   # See also third-party/TARGETS for buck's typing-extensions version.
   "typing-extensions>=4.10.0",
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 297d275870f..1743b142a4d 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,6 +1,6 @@
 # Pip packages needed to build from source. Mainly for development of ExecuTorch.
 
-cmake>=3.19, <4.0.0  # For building binary targets in the wheel.
+cmake>=3.29, <4.0.0  # For building binary targets in the wheel.
 pip>=23  # For building the pip package.
 pyyaml  # Imported by the kernel codegen tools.
 setuptools>=63  # For building the pip package contents.
diff --git a/requirements-examples.txt b/requirements-examples.txt
index f52eb113075..0e0cf95eae9 100644
--- a/requirements-examples.txt
+++ b/requirements-examples.txt
@@ -1,6 +1,9 @@
 # pip packages needed to run examples.
 # TODO: Make each example publish its own requirements.txt
+datasets == 3.6.0 # 4.0.0 deprecates trust_remote_code and load scripts. For now pin to 3.6.0
 timm == 1.0.7
 torchsr == 1.0.4
 torchtune >= 0.6.1
 transformers ==4.47.1
+torchaudio==2.8.0
+torchvision==0.23.0
diff --git a/runtime/core/portable_type/c10/README.md b/runtime/core/portable_type/c10/README.md
index 361d4b51c44..c0fa01d7456 100644
--- a/runtime/core/portable_type/c10/README.md
+++ b/runtime/core/portable_type/c10/README.md
@@ -12,7 +12,7 @@ would cause all headers in that directory to be includeable with
 `runtime/core/portable_type/complex.h`, which would shadow the C99
 `complex.h` standard header.
 
-`torch/standalone` has been added as an extra "even more bottom of
+`torch/headeronly` has been added as an extra "even more bottom of
 stack" directory in PyTorch, so we have to add it to our sync
 here. The extra "stutter" c10 directory causing `c10/torch/standlone`
 is unfortunately awkward; perhaps we can rename the top-level
diff --git a/runtime/core/portable_type/c10/c10/macros/Export.h b/runtime/core/portable_type/c10/c10/macros/Export.h
index 28068bd34d4..b013910902b 100644
--- a/runtime/core/portable_type/c10/c10/macros/Export.h
+++ b/runtime/core/portable_type/c10/c10/macros/Export.h
@@ -5,7 +5,7 @@
 #include <c10/macros/cmake_macros.h>
 #endif // C10_USING_CUSTOM_GENERATED_MACROS
 
-#include <torch/standalone/macros/Export.h>
+#include <torch/headeronly/macros/Export.h>
 
 // This one is being used by libtorch.so
 #ifdef CAFFE2_BUILD_MAIN_LIB
diff --git a/runtime/core/portable_type/c10/c10/targets.bzl b/runtime/core/portable_type/c10/c10/targets.bzl
index 6b5c3bd8bd1..3ea50c25555 100644
--- a/runtime/core/portable_type/c10/c10/targets.bzl
+++ b/runtime/core/portable_type/c10/c10/targets.bzl
@@ -125,7 +125,7 @@ def define_common_targets():
                 "@EXECUTORCH_CLIENTS",
             ],
             exported_deps = [
-                "//executorch/runtime/core/portable_type/c10/torch/standalone:torch_standalone_headers",
+                "//executorch/runtime/core/portable_type/c10/torch/headeronly:torch_headeronly",
             ] + select({
                 "DEFAULT": [],
                 # Half-inl.h depends on vec_half.h from ATen, but only when building for x86.
diff --git a/runtime/core/portable_type/c10/torch/standalone/TARGETS b/runtime/core/portable_type/c10/torch/headeronly/TARGETS
similarity index 100%
rename from runtime/core/portable_type/c10/torch/standalone/TARGETS
rename to runtime/core/portable_type/c10/torch/headeronly/TARGETS
diff --git a/runtime/core/portable_type/c10/torch/standalone/macros/Export.h b/runtime/core/portable_type/c10/torch/headeronly/macros/Export.h
similarity index 100%
rename from runtime/core/portable_type/c10/torch/standalone/macros/Export.h
rename to runtime/core/portable_type/c10/torch/headeronly/macros/Export.h
diff --git a/runtime/core/portable_type/c10/torch/standalone/targets.bzl b/runtime/core/portable_type/c10/torch/headeronly/targets.bzl
similarity index 81%
rename from runtime/core/portable_type/c10/torch/standalone/targets.bzl
rename to runtime/core/portable_type/c10/torch/headeronly/targets.bzl
index 1faf1173a4a..0ddce72f237 100644
--- a/runtime/core/portable_type/c10/torch/standalone/targets.bzl
+++ b/runtime/core/portable_type/c10/torch/headeronly/targets.bzl
@@ -8,7 +8,7 @@ def define_common_targets():
     """
 
     runtime.cxx_library(
-        name = "torch_standalone_headers",
+        name = "torch_headeronly",
         exported_headers = glob(["**/*.h"]),
-        header_namespace = "torch/standalone",
+        header_namespace = "torch/headeronly",
     )
diff --git a/runtime/executor/program.cpp b/runtime/executor/program.cpp
index 238c806b1d6..e58c8a96aa7 100644
--- a/runtime/executor/program.cpp
+++ b/runtime/executor/program.cpp
@@ -172,7 +172,18 @@ Result<executorch_flatbuffer::ExecutionPlan*> get_execution_plan(
   // only offset, the constant segment is empty and does not need to be loaded.
   const auto* constant_segment = flatbuffer_program->constant_segment();
   if (constant_segment != nullptr && constant_segment->offsets() != nullptr &&
-      constant_segment->offsets()->size() > 1) {
+      constant_segment->offsets()->size() > 0) {
+    if (constant_segment->offsets()->size() == 1) {
+      // No constants; the constant segment is empty and does not
+      // need to be loaded.
+      return Program(
+          loader,
+          segment_base_offset,
+          std::move(program_data.get()),
+          flatbuffer_program,
+          /*constant_segment_data=*/FreeableBuffer{},
+          std::move(pte_data_map));
+    }
     // The constant data is inside a separate segment.
     const auto* constant_buffer = flatbuffer_program->constant_buffer();
     ET_CHECK_OR_RETURN_ERROR(
@@ -219,6 +230,16 @@ Result<executorch_flatbuffer::ExecutionPlan*> get_execution_plan(
   } else {
     // The constant data is stored inside the flatbuffer, so this program does
     // not contain a separate segment for it.
+
+    // NOTE: This branch is deprecated from ExecuTorch 0.7 onwards.
+    // Please regenerate your PTE file to ensure newer ExecuTorch runtimes can
+    // support it. ExecuTorch deprecation policy:
+    // https://docs.pytorch.org/executorch/stable/api-life-cycle.html#deprecation-policy.
+    // For support, contact the PyTorch Edge team or make an issue in:
+    // https://github.com/pytorch/executorch/issues.
+    ET_LOG(
+        Error,
+        "!!DEPRECATED!! This branch is deprecated from ExecuTorch 0.7; re-export this PTE file to ensure support on newer runtimes.");
     return Program(
         loader,
         segment_base_offset,
diff --git a/scripts/build_apple_frameworks.sh b/scripts/build_apple_frameworks.sh
index b98832a2266..727670daf29 100755
--- a/scripts/build_apple_frameworks.sh
+++ b/scripts/build_apple_frameworks.sh
@@ -50,6 +50,7 @@ libmpsdelegate.a,\
 
 FRAMEWORK_BACKEND_XNNPACK="backend_xnnpack:\
 libXNNPACK.a,\
+libkleidiai.a,\
 libxnnpack_backend.a,\
 libxnnpack-microkernels-prod.a,\
 :"
@@ -148,10 +149,13 @@ for preset_index in "${!PRESETS[@]}"; do
     echo "Building preset ${preset} (${mode}) in ${preset_output_dir}..."
 
     # Do NOT add options here. Update the respective presets instead.
+    # Xcode multi-config presets leave CMAKE_BUILD_TYPE empty, so force EXECUTORCH_ENABLE_LOGGING per-mode.
     cmake -S "${SOURCE_ROOT_DIR}" \
           -B "${preset_output_dir}" \
           -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY="${preset_output_dir}" \
           -DCMAKE_BUILD_TYPE="${mode}" \
+          -UEXECUTORCH_ENABLE_LOGGING \
+          -DEXECUTORCH_ENABLE_LOGGING=$([ "${mode}" = "Debug" ] && echo ON || echo OFF) \
           ${CMAKE_OPTIONS_OVERRIDE[@]:-} \
           --preset "${preset}"
 
@@ -180,7 +184,7 @@ sed -i '' '1i\
 ' \
 "$HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/c10/macros/Macros.h" \
 "$HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/c10/macros/Export.h" \
-"$HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/torch/standalone/macros/Export.h"
+"$HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/torch/headeronly/macros/Export.h"
 
 cp -r $HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/c10 "$HEADERS_ABSOLUTE_PATH/"
 cp -r $HEADERS_ABSOLUTE_PATH/executorch/runtime/core/portable_type/c10/torch "$HEADERS_ABSOLUTE_PATH/"
diff --git a/third-party/ao b/third-party/ao
index bc68b11f1bf..442232fbfb0 160000
--- a/third-party/ao
+++ b/third-party/ao
@@ -1 +1 @@
-Subproject commit bc68b11f1bf77be38721ca7dd2c477aeb5e6626e
+Subproject commit 442232fbfb0f6cdfdb9c3eac20f57e5a746ee1bf
diff --git a/tools/cmake/preset/default.cmake b/tools/cmake/preset/default.cmake
index f2733f591eb..9afefd4e372 100644
--- a/tools/cmake/preset/default.cmake
+++ b/tools/cmake/preset/default.cmake
@@ -258,7 +258,7 @@ define_overridable_option(
 define_overridable_option(
   EXECUTORCH_XNNPACK_ENABLE_KLEIDI
   "Enable Arm Kleidi kernels"
-  BOOL OFF
+  BOOL ON
 )
 # Turning this on cache weights between partitions and methods. If weights
 # are shared across methods/partitions then this can reduce load time and
diff --git a/tools/cmake/preset/llm.cmake b/tools/cmake/preset/llm.cmake
index da1364eb2ad..88148ee6cfd 100644
--- a/tools/cmake/preset/llm.cmake
+++ b/tools/cmake/preset/llm.cmake
@@ -20,6 +20,9 @@ set_overridable_option(EXECUTORCH_BUILD_XNNPACK ON)
 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
   set_overridable_option(EXECUTORCH_BUILD_COREML ON)
   set_overridable_option(EXECUTORCH_BUILD_MPS ON)
+  if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+    set_overridable_option(EXECUTORCH_BUILD_TORCHAO ON)
+  endif()
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
   # Linux-specific code here
 elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME STREQUAL "WIN32")
