Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .ci/scripts/build-cadence-xtensa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
#
# Cross-compile cadence_executor_runner for a Cadence Xtensa core and (by
# default) smoke-test it on the Instruction Set Simulator with a trivial model.
#
# Requires the Xtensa toolchain env to already be set (run
# .ci/scripts/setup-xtensa-tools.sh <backend> first): XTENSA_TOOLCHAIN,
# TOOLCHAIN_VER, XTENSA_SYSTEM, XTENSA_CORE, XTENSAD_LICENSE_FILE,
# CADENCE_OPT_FLAG, and xt-clang on PATH.
#
# Usage:
# .ci/scripts/build-cadence-xtensa.sh [--no-run]
# --no-run : compile only, skip the ISS smoke test

set -euo pipefail

RUN_SMOKE=1
[[ "${1:-}" == "--no-run" ]] && RUN_SMOKE=0

: "${XTENSA_TOOLCHAIN:?run setup-xtensa-tools.sh first}"
: "${TOOLCHAIN_VER:?run setup-xtensa-tools.sh first}"
: "${XTENSA_CORE:?run setup-xtensa-tools.sh first}"
: "${CADENCE_OPT_FLAG:?run setup-xtensa-tools.sh first}"

NPROC=$(nproc)
echo "=== building cadence_executor_runner for ${XTENSA_CORE} (${CADENCE_OPT_FLAG}) ==="
xt-clang --version | head -1

rm -rf cmake-out
CXXFLAGS="-fno-exceptions -fno-rtti" cmake \
-DCMAKE_TOOLCHAIN_FILE=./backends/cadence/cadence.cmake \
-DCMAKE_INSTALL_PREFIX=cmake-out \
-DCMAKE_BUILD_TYPE=Release \
-DEXECUTORCH_BUILD_CADENCE=ON \
"-D${CADENCE_OPT_FLAG}=ON" \
-DEXECUTORCH_BUILD_PORTABLE_OPS=ON \
-DEXECUTORCH_BUILD_CADENCE_RUNNER=ON \
-DEXECUTORCH_BUILD_EXECUTOR_RUNNER=OFF \
-DEXECUTORCH_BUILD_EXTENSION_RUNNER_UTIL=ON \
-DEXECUTORCH_ENABLE_LOGGING=ON \
-DEXECUTORCH_BUILD_PTHREADPOOL=OFF \
-DEXECUTORCH_BUILD_CPUINFO=OFF \
-DEXECUTORCH_USE_DL=OFF \
-DEXECUTORCH_BUILD_KERNELS_LLM=OFF \
-DEXECUTORCH_BUILD_DEVTOOLS=OFF \
-DHAVE_FNMATCH_H=OFF \
-DFLATCC_ALLOW_WERROR=OFF \
-DPYTHON_EXECUTABLE="$(which python3)" \
-Bcmake-out .

cmake --build cmake-out --target cadence_executor_runner -j"${NPROC}"

RUNNER="cmake-out/backends/cadence/cadence_executor_runner"
if [[ ! -f "${RUNNER}" ]]; then
echo "ERROR: ${RUNNER} was not produced" >&2
exit 1
fi
command -v file >/dev/null 2>&1 && file "${RUNNER}" || true
echo "Build OK: ${RUNNER}"

if [[ "${RUN_SMOKE}" == "0" ]]; then
echo "Skipping ISS smoke test (--no-run)."
exit 0
fi

echo "=== ISS smoke test: export add.pte and run on xt-run --turbo ==="
python3 -m examples.portable.scripts.export --model_name=add >/dev/null
LOG=$(mktemp)
xt-run --turbo "${RUNNER}" --model_path=add.pte 2>&1 | tee "${LOG}"
if ! grep -q "Model executed successfully" "${LOG}"; then
echo "ERROR: ISS smoke test did not report success for ${XTENSA_CORE}" >&2
exit 1
fi
echo "ISS smoke test passed for ${XTENSA_CORE}."
164 changes: 164 additions & 0 deletions .ci/scripts/setup-xtensa-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
#
# Download and install the licensed Cadence Xtensa toolchain + core config for
# a given backend, then export the environment that
# backends/cadence/cadence.cmake and xt-run need.
#
# The artifacts (host tools, the core tarball, and the bundled license) cannot
# be hosted publicly, so they are fetched at runtime from an auth-gated object
# store. The store location is provided by the caller via XTENSA_S3_BUCKET (set
# from a CI variable); credentials are obtained out of band before this runs.
#
# Usage:
# XTENSA_S3_BUCKET=<bucket> .ci/scripts/setup-xtensa-tools.sh <backend>
# backend = hifi4 | vision | fusion_g3
#
# In GitHub Actions this appends the toolchain env to $GITHUB_ENV so later
# steps inherit it. Run locally to populate a workspace for manual builds.
#
# Modeled on .ci/scripts/setup-arm-baremetal-tools.sh.

set -euo pipefail

BACKEND="${1:-}"
if [[ -z "${BACKEND}" ]]; then
echo "ERROR: usage: XTENSA_S3_BUCKET=<bucket> $0 <hifi4|vision|fusion_g3>" >&2
exit 1
fi

S3_BUCKET="${XTENSA_S3_BUCKET:-}"
if [[ -z "${S3_BUCKET}" ]]; then
echo "ERROR: XTENSA_S3_BUCKET is not set (provide it from a CI variable)." >&2
exit 1
fi
# Objects live flat at the bucket root by default; set these to put toolchains
# and cores under key prefixes instead.
S3_TOOLCHAIN_PREFIX="${XTENSA_S3_TOOLCHAIN_PREFIX:-}"
S3_CORE_PREFIX="${XTENSA_S3_CORE_PREFIX:-}"

# Per-backend mapping: core tarball, toolchain tarball, core name, OPT flag.
# The toolchain's clang major must match the core's codegen plugin:
# hifi4 / fusion_g3 cores (RI-2022.10, clang 10) -> RI-2022.9 host tools
# vision core (RJ-2025.5, clang 15) -> RJ-2025.5 host tools
case "${BACKEND}" in
hifi4)
CORE_NAME="hifi4_ss_spfpu_7_et_ci2"
CORE_TARBALL="hifi4_ss_spfpu_7_et_ci2_linux.tgz"
TOOLCHAIN_TARBALL="XtensaTools_RI_2022_9_linux.tgz"
TOOLCHAIN_VER="RI-2022.9-linux"
OPT_FLAG="EXECUTORCH_NNLIB_OPT"
;;
fusion_g3)
CORE_NAME="XRC_FuG3_TYP_SPVFPU_et_c2"
CORE_TARBALL="XRC_FuG3_TYP_SPVFPU_et_c2_linux.tgz"
TOOLCHAIN_TARBALL="XtensaTools_RI_2022_9_linux.tgz"
TOOLCHAIN_VER="RI-2022.9-linux"
OPT_FLAG="EXECUTORCH_FUSION_G3_OPT"
;;
vision)
CORE_NAME="XRC_Vision_110_AO_et_ci2"
CORE_TARBALL="XRC_Vision_110_AO_et_ci2_linux.tgz"
TOOLCHAIN_TARBALL="XtensaTools_RJ_2025_5_linux.tgz"
TOOLCHAIN_VER="RJ-2025.5-linux"
OPT_FLAG="EXECUTORCH_VISION_OPT"
;;
*)
echo "ERROR: unknown backend '${BACKEND}' (expected hifi4|vision|fusion_g3)" >&2
exit 1
;;
esac

XTENSA_ROOT="${XTENSA_ROOT:-/tmp/xtensa}"
TOOLS_ROOT="${XTENSA_ROOT}/tools" # contains <ver>-linux/XtensaTools
CORES_ROOT="${XTENSA_ROOT}/cores" # contains <corever>-linux/<core>
REGISTRY_ROOT="${XTENSA_ROOT}/registry/${CORE_NAME}"
DL_DIR="${XTENSA_ROOT}/download"
mkdir -p "${TOOLS_ROOT}" "${CORES_ROOT}" "${REGISTRY_ROOT}" "${DL_DIR}"

s3_get() {
# $1 = s3 key, $2 = local dest
local key="$1" dest="$2"
echo "Downloading s3://${S3_BUCKET}/${key} ..."
aws s3 cp "s3://${S3_BUCKET}/${key}" "${dest}" --only-show-errors
}

extract_tgz() {
# $1 = .tgz, $2 = dest dir. Some vendor core tarballs carry trailing bytes
# after a valid gzip stream; gzip then exits 2 ("trailing garbage ignored")
# even though the archive decompressed fully, which aborts `tar xzf`. Key the
# success check off tar's exit, not gzip's.
local tgz="$1" dest="$2" rc
set +o pipefail
gzip -dc "${tgz}" 2>/dev/null | tar xf - -C "${dest}"
rc=${PIPESTATUS[1]}
set -o pipefail
[[ "${rc}" -eq 0 ]] || { echo "ERROR: failed to extract ${tgz} (tar rc=${rc})" >&2; exit 1; }
}

# 1. Toolchain (host xt-clang/xt-run). Skip re-extract if already present.
if [[ ! -d "${TOOLS_ROOT}/${TOOLCHAIN_VER}/XtensaTools" ]]; then
s3_get "${S3_TOOLCHAIN_PREFIX:+${S3_TOOLCHAIN_PREFIX}/}${TOOLCHAIN_TARBALL}" "${DL_DIR}/${TOOLCHAIN_TARBALL}"
extract_tgz "${DL_DIR}/${TOOLCHAIN_TARBALL}" "${TOOLS_ROOT}"
fi
TOOLCHAIN_HOME="${TOOLS_ROOT}/${TOOLCHAIN_VER}/XtensaTools"
if [[ ! -x "${TOOLCHAIN_HOME}/bin/xt-clang" ]]; then
echo "ERROR: xt-clang not found at ${TOOLCHAIN_HOME}/bin after extract" >&2
exit 1
fi

# 2. Core config (ISA libs, params, examples, bundled magic-key license).
s3_get "${S3_CORE_PREFIX:+${S3_CORE_PREFIX}/}${CORE_TARBALL}" "${DL_DIR}/${CORE_TARBALL}"
extract_tgz "${DL_DIR}/${CORE_TARBALL}" "${CORES_ROOT}"
CORE_DIR=$(echo "${CORES_ROOT}"/*/"${CORE_NAME}")
if [[ ! -d "${CORE_DIR}" ]]; then
echo "ERROR: core dir for ${CORE_NAME} not found under ${CORES_ROOT}" >&2
exit 1
fi

# 3. Build a local Xtensa core registry with the XPG-internal build paths in
# the params file rewritten to our extracted toolchain + core locations.
# The vendor ships params referencing /././home/xpgcust/... build paths.
PARAMS_SRC="${CORE_DIR}/config/${CORE_NAME}-params"
TOOLS_PFX=$(sed -n 's/^install-prefix = //p' "${PARAMS_SRC}" | head -1)
TOOLSUB_PFX=$(sed -n 's/^xtensa-tools = //p' "${PARAMS_SRC}" | head -1)
CFG_PFX=$(sed -n 's/^config-prefix = //p' "${PARAMS_SRC}" | head -1)
sed \
-e "s|${TOOLS_PFX}|${TOOLCHAIN_HOME}|g" \
-e "s|${TOOLSUB_PFX}|${TOOLCHAIN_HOME}/Tools|g" \
-e "s|${CFG_PFX}|${CORE_DIR}|g" \
"${PARAMS_SRC}" > "${REGISTRY_ROOT}/${CORE_NAME}-params"
ln -sf "${CORE_NAME}-params" "${REGISTRY_ROOT}/default-params"

LICENSE_FILE="${CORE_DIR}/misc/license.dat"

# 4. Export environment. cadence.cmake reads XTENSA_TOOLCHAIN/TOOLCHAIN_VER;
# xt-clang/xt-run read XTENSA_SYSTEM/XTENSA_CORE; xtensad reads
# XTENSAD_LICENSE_FILE (the bundled uncounted magic key, no server needed).
emit() {
# Export into the current shell (so callers that `source` this script get the
# vars) and append to $GITHUB_ENV (so later workflow steps inherit them too).
echo "$1"
export "${1?}"
if [[ -n "${GITHUB_ENV:-}" ]]; then echo "$1" >> "${GITHUB_ENV}"; fi
}
echo "=== Xtensa env for backend '${BACKEND}' (core ${CORE_NAME}) ==="
emit "XTENSA_TOOLCHAIN=${TOOLS_ROOT}"
emit "TOOLCHAIN_VER=${TOOLCHAIN_VER}"
emit "XTENSA_SYSTEM=${REGISTRY_ROOT}"
emit "XTENSA_CORE=${CORE_NAME}"
emit "XTENSAD_LICENSE_FILE=${LICENSE_FILE}"
emit "CADENCE_OPT_FLAG=${OPT_FLAG}"
if [[ -n "${GITHUB_PATH:-}" ]]; then
echo "${TOOLCHAIN_HOME}/bin" >> "${GITHUB_PATH}"
fi
export PATH="${TOOLCHAIN_HOME}/bin:${PATH}"

echo "=== sanity ==="
xt-clang --version 2>&1 | head -1
xt-run --show-config=cores 2>&1 | sed -n '/available/,/registry/p' | head -6
echo "Xtensa toolchain ready for ${BACKEND}."
94 changes: 94 additions & 0 deletions .github/workflows/_xtensa_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Reusable: cross-compile cadence_executor_runner for one Cadence Xtensa core.
#
# A native job (not linux_job_v2) because the GitHub OIDC token must be minted on
# the runner host: the ACTIONS_ID_TOKEN_REQUEST_* vars do not cross into
# linux_job_v2's docker exec. So the role is assumed on the host, then the build
# runs inside the CI image via docker run with the creds passed in. Binding the
# environment also gives the OIDC token the environment claim. The licensed
# toolchain + core configs are fetched at runtime from an auth-gated store;
# role/region/store come from CI variables and are not committed.
name: xtensa-build

on:
workflow_call:
inputs:
backend:
description: "Cadence backend to build (hifi4 | vision | fusion_g3)"
required: true
type: string
ref:
description: "Git ref to check out"
required: false
type: string
default: ""

jobs:
build:
name: ${{ inputs.backend }}
runs-on: linux.2xlarge
environment: cadence
permissions:
id-token: write
contents: read
steps:
- name: Checkout executorch
uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{ inputs.ref }}

- name: Calculate docker image
id: calculate-docker-image
uses: pytorch/test-infra/.github/actions/calculate-docker-image@main
with:
docker-image-name: ci-image:executorch-ubuntu-22.04-clang12

- name: Pull docker image
run: docker pull "${{ steps.calculate-docker-image.outputs.docker-image }}"

- name: Assume Cadence artifacts role (host OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.CADENCE_CI_AWS_ROLE }}
aws-region: ${{ vars.CADENCE_CI_AWS_REGION }}

- name: Cross-compile cadence_executor_runner
env:
DOCKER_IMAGE: ${{ steps.calculate-docker-image.outputs.docker-image }}
BACKEND: ${{ inputs.backend }}
XTENSA_S3_BUCKET: ${{ vars.CADENCE_CI_S3_BUCKET }}
shell: bash
run: |
set -eux
# OIDC/role assumption already happened on the host above; pass the
# resulting AWS creds and the store/backend into the CI image, where
# the toolchain download + cross-compile run.
docker run --rm \
-e BACKEND -e XTENSA_S3_BUCKET \
-e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN \
-e AWS_DEFAULT_REGION -e AWS_REGION \
-v "${GITHUB_WORKSPACE}:/work/executorch" -w /work/executorch \
"${DOCKER_IMAGE}" \
bash -c '
set -exo pipefail
eval "$(/opt/conda/bin/conda shell.bash hook)"
conda activate "$(conda env list --json | jq -r ".envs | .[-1]")"
./install_requirements.sh > /dev/null
pip install --quiet awscli
# hifi4/fusion_g3 optimized kernels need the foss-xtensa nnlib
# sources, which are not vendored in executorch; the cadence
# installer clones them. vision has no nnlib dependency.
if [ "${BACKEND}" != "vision" ]; then
backends/cadence/install_requirements.sh
fi
source .ci/scripts/setup-xtensa-tools.sh "${BACKEND}"
.ci/scripts/build-cadence-xtensa.sh --no-run
chmod -R a+rX cmake-out
'

- name: Upload runner
uses: actions/upload-artifact@v4
with:
name: cadence-xtensa-build-${{ inputs.backend }}
path: cmake-out/backends/cadence/cadence_executor_runner
if-no-files-found: error
22 changes: 22 additions & 0 deletions .github/workflows/build-cadence-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,25 @@ jobs:
uses: ./.github/workflows/_test_cadence.yml
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}

# Cross-compile cadence_executor_runner for each Cadence Xtensa core, one job
# per backend so they show as separate lines (no matrix grouping). Shared logic
# lives in _xtensa_build.yml. fusion_g3 is omitted until the upstream fusion_g3
# <-> nnlib-FusionG3 API skew is fixed (its runner does not link).
hifi-build:
permissions:
id-token: write
contents: read
uses: ./.github/workflows/_xtensa_build.yml
with:
backend: hifi4
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}

vision-build:
permissions:
id-token: write
contents: read
uses: ./.github/workflows/_xtensa_build.yml
with:
backend: vision
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
Loading
Loading