Skip to content
Closed
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
file "${RUNNER}"
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}."
149 changes: 149 additions & 0 deletions .ci/scripts/setup-xtensa-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/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
S3_TOOLCHAIN_PREFIX="${XTENSA_S3_TOOLCHAIN_PREFIX:-toolchains}"
S3_CORE_PREFIX="${XTENSA_S3_CORE_PREFIX:-cores}"

# 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
}

# 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}/${TOOLCHAIN_TARBALL}" "${DL_DIR}/${TOOLCHAIN_TARBALL}"
tar xzf "${DL_DIR}/${TOOLCHAIN_TARBALL}" -C "${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}/${CORE_TARBALL}" "${DL_DIR}/${CORE_TARBALL}"
tar xzf "${DL_DIR}/${CORE_TARBALL}" -C "${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}."
64 changes: 64 additions & 0 deletions .github/workflows/build-cadence-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,67 @@ 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 the Cadence Xtensa cores (build
# stage; an ISS test stage follows later, cf. cpu-build -> cpu-test). The
# toolchain + core configs are licensed and cannot be hosted publicly, so they
# are fetched at runtime from an auth-gated store using a short-lived OIDC
# credential; the store, role, and region are supplied via CI variables and
# are not committed. Fork PRs are skipped (they cannot assume the role).
xtensa-build:
name: xtensa-build-${{ matrix.backend }}
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }}
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix:
# fusion_g3 is omitted until the upstream fusion_g3 operator <->
# nnlib-FusionG3 API skew is resolved (its op wrappers call kernels
# absent from the vendored nnlib, so the runner does not link).
backend: [hifi4, vision]
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
with:
job-name: xtensa-build-${{ matrix.backend }}
runner: linux.2xlarge
docker-image: ci-image:executorch-ubuntu-22.04-clang12
submodules: recursive
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
timeout: 90
upload-artifact: cadence-xtensa-build-${{ matrix.backend }}
script: |
set -eux
# The generic Linux job uses the base env, not the one set up by the image.
CONDA_ENV=$(conda env list --json | jq -r ".envs | .[-1]")
conda activate "${CONDA_ENV}"

./install_requirements.sh > /dev/null
pip install --quiet awscli

# Obtain a short-lived credential for the artifact store via GitHub
# OIDC. The ACTIONS_ID_TOKEN_REQUEST_* vars are present because this job
# has id-token: write. Role and store are provided via CI variables.
OIDC_TOKEN=$(curl -sS \
-H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=sts.amazonaws.com" | jq -r '.value')
CREDS=$(aws sts assume-role-with-web-identity \
--role-arn "${{ vars.CADENCE_CI_AWS_ROLE }}" \
--role-session-name "xtensa-${{ matrix.backend }}-${GITHUB_RUN_ID}" \
--web-identity-token "${OIDC_TOKEN}" \
--duration-seconds 7200 \
--query Credentials --output json)
export AWS_ACCESS_KEY_ID=$(echo "${CREDS}" | jq -r .AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo "${CREDS}" | jq -r .SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo "${CREDS}" | jq -r .SessionToken)
export AWS_DEFAULT_REGION="${{ vars.CADENCE_CI_AWS_REGION }}"
export XTENSA_S3_BUCKET="${{ vars.CADENCE_CI_S3_BUCKET }}"

# Download + install toolchain and core, exporting the Xtensa env into
# this shell, then cross-compile cadence_executor_runner (build only;
# the ISS smoke run lives in the downstream test job).
source .ci/scripts/setup-xtensa-tools.sh "${{ matrix.backend }}"
.ci/scripts/build-cadence-xtensa.sh --no-run

# Publish the cross-compiled runner for the downstream test job.
cp cmake-out/backends/cadence/cadence_executor_runner "${RUNNER_ARTIFACT_DIR}/"
35 changes: 35 additions & 0 deletions backends/cadence/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,38 @@ else()
endif()

add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${TARGET_DIR}/operators)

# Cadence executor_runner: cross-compiled ExecuTorch runner for the Xtensa ISS
# (xt-run / xt-run --turbo). Self-contained, gflags-free argv parser, reads .pte
# via xt-run semi-hosting.
#
# Usage: cmake ... -DEXECUTORCH_BUILD_CADENCE_RUNNER=ON xt-run --turbo
# cmake-out/backends/cadence/cadence_executor_runner \ --model_path=add.pte
if(EXECUTORCH_BUILD_CADENCE_RUNNER)
add_executable(cadence_executor_runner cadence_executor_runner.cpp)
target_compile_definitions(
cadence_executor_runner PRIVATE ET_ENABLE_ENUM_STRINGS=0
)
target_include_directories(
cadence_executor_runner
PRIVATE ${_common_include_directories} ${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/include
)
# Mirror the upstream executor_runner cadence link list (top-level
# CMakeLists.txt: list(APPEND _executor_runner_libs cadence_ops_lib)). Do NOT
# add --whole-archive: cadence_ops_lib is also pulled transitively, and
# forcing a second copy double-runs its static kernel-registration
# initializers and asserts at runtime.
target_link_libraries(
cadence_executor_runner PRIVATE executorch extension_evalue_util
extension_runner_util cadence_ops_lib
)
# Vision and Fusion-G3 ops (e.g. op_softmax) reference iDMA scheduling symbols
# and those cores ship libidma in their LSP. HiFi4 and generic cores do not
# use iDMA and their LSPs may not provide libidma, so only link it for the
# cores that need it.
if(EXECUTORCH_VISION_OPT OR EXECUTORCH_FUSION_G3_OPT)
target_link_options(cadence_executor_runner PRIVATE -lidma)
endif()
target_link_options(cadence_executor_runner PRIVATE -static -lm)
endif()
Loading
Loading