DragonArray docs
Build TensorFlow 2.21.0 for LoongArch from source
DragonArray's TensorFlow patch adds the LoongArch Bazel platform and wheel tag, CPython 3.13 toolchain support, GCC 15 compatibility fixes, old-world build shims, sparse BF16 fixes, and the Eigen convolution changes used by the LASX path.
The same patch supports three CPU profiles, but the compiler flags are not interchangeable. Choose one profile and use it consistently for TensorFlow, the static C++ runtime, NumPy, and every other native dependency.
Source revision and patch
Build this exact TensorFlow revision:
tag: v2.21.0
commit: a481b10260dfdf833a1b16007eead49c1d7febf3
Download the
tensorflow-2.21.0-loongarch64.patch.gz
patch. The published file has these checksums:
compressed .gz: b6804b551a01693c517eab7f26f7bd571e558b52df94c3b87425f9a45a8ad9af
uncompressed: 15de4da62c2d1309a39362a7a44cf2a9f948cab7aaf9abc772a969b4e47134d4
Start with a clean checkout and apply the consolidated patch once:
git clone --branch v2.21.0 --depth 1 \
https://github.com/tensorflow/tensorflow.git tensorflow-2.21.0
cd tensorflow-2.21.0
test "$(git rev-parse HEAD)" = \
a481b10260dfdf833a1b16007eead49c1d7febf3
curl --fail --location --output tensorflow.patch.gz \
https://dragon-array.org/download/tensorflow-2.21.0-loongarch64.patch.gz
printf '%s %s\n' \
b6804b551a01693c517eab7f26f7bd571e558b52df94c3b87425f9a45a8ad9af \
tensorflow.patch.gz | sha256sum -c -
gzip -dc tensorflow.patch.gz > tensorflow.patch
printf '%s %s\n' \
15de4da62c2d1309a39362a7a44cf2a9f948cab7aaf9abc772a969b4e47134d4 \
tensorflow.patch | sha256sum -c -
git apply --check tensorflow.patch
git apply tensorflow.patch
git diff --check
git apply --reverse --check tensorflow.patch
The final command is read-only: it proves that the complete patch is present. Do not stack older incremental TensorFlow or XLA patches on top of this file; their changes are already included. Patches for h5py, grpcio, and prepared Bazel override repositories remain separate because they modify other source trees.
Choose the CPU profile
Use lscpu on the target system if the processor model is not known:
LC_ALL=C lscpu | grep -E 'Architecture|Model name|Flags'
| Target systems | ISA level | Required compiler flags | Example Bazel output base |
|---|---|---|---|
| Loongson 3A5000 and 3B5000 | LA464, LSX only | -march=la464 -mtune=la464 -mlsx -mno-lasx |
~/.cache/bazel/tf-2.21.0-la464-lsx |
| Loongson 3C5000 | LA464, LASX | -march=la464 -mtune=la464 -mlasx |
~/.cache/bazel/tf-2.21.0-la464-lasx |
| Loongson 3A6000, 3B6000, and 3C6000 series | LA664, LASX | -march=la664 -mtune=la664 -mlasx |
~/.cache/bazel/tf-2.21.0-la664-lasx |
Every profile also uses -O2 -g0 -fPIC -mcmodel=medium for native release
builds.
The differences matter:
- 3A5000/3B5000: keep both
-mlsxand-mno-lasx. The explicit negative flag prevents a LASX-capable build host from leaking LASX instructions into an LSX-only wheel. - 3C5000: use
-march=la464, not-march=la664, and add-mlasx. This retains LA464 compatibility while enabling the chip's LASX unit. - 3×6000 series: use
-march=la664 -mtune=la664 -mlasx. A wheel built this way is not an LA464 wheel and must not be installed on a 5000-series system.
Do not use -march=native for a redistributable wheel. Apply the selected ISA
flags to target and host C/C++ actions, the static libstdc++.a, TensorFlow,
NumPy, HDF5/h5py, grpcio, ml_dtypes, and optree. Keep a separate build tree,
virtual environment, wheel directory, and Bazel output base for every profile.
Tested build environment
The DragonArray wheels were produced with:
GCC 15.2.0 /opt/loongson-gcc-15.2.0
Bazel 8.4.2
CMake 4.3.2
Ninja 1.10
OpenJDK 21.0.10
pyenv 2.8.1
build Python 3.13.13
validation Python 3.13.14
Allow at least 15 GiB of RAM plus swap and 60 GiB of free disk space for an isolated Bazel output tree. The reference builds use four jobs and a 10 GB Bazel memory budget. They use the old-world LoongArch LP64 ABI and glibc 2.28.
A LASX-capable 3A6000 can build all three profiles. The 3A5000 profile is safe
only when -mno-lasx is applied everywhere. Code produced for LA664 cannot be
executed for validation on an LA464 machine.
Prepare Python and source inputs
TensorFlow 2.21.0 supports CPython 3.13. The reference build uses CPython 3.13.13 and validates the finished wheel in a clean CPython 3.13.14 environment:
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
pyenv install 3.13.13
pyenv install 3.13.14
The Bazel build uses a hermetic CPython 3.13.13 archive with a top-level
python/ directory:
python-3.13.13-loongarch64.tar.gz
SHA-256 ca540b5ab4804d27fb8f6ddae1e4792fcf2b4050dfc88f22ed227fd5fc673b84
Pass it through these repository settings:
--repo_env=HERMETIC_PYTHON_VERSION=3.13
--repo_env=HERMETIC_PYTHON_URL=file:///path/to/python-3.13.13-loongarch64.tar.gz
--repo_env=HERMETIC_PYTHON_SHA256=ca540b5ab4804d27fb8f6ddae1e4792fcf2b4050dfc88f22ed227fd5fc673b84
--repo_env=HERMETIC_PYTHON_PREFIX=python
The complete wheel set also requires source builds of:
- NumPy 2.5.1;
- HDF5 1.14.6 and h5py 3.14.0;
- ml_dtypes 0.5.4;
- optree 0.19.1;
- grpcio 1.83.0.
The experimental NumPy variant is built from
adamdeprince/numpy, branch loongson-experimental,
pinned by the reference build to commit
79b033101a38a0f1fa2c6b94380f31041f74da55.
Prepare the Bazel overrides
The TensorFlow patch enables the LoongArch build, but TensorFlow's external repository graph still needs LoongArch-capable source snapshots. The reference build supplies prepared overrides for:
platforms
rules_python
eigen_archive
ducc
tf_runtime
io_bazel_rules_closure
build_bazel_rules_android
local_execution_config_python
local_config_python
local_execution_config_platform
system_python
cpuinfo
skcms
png
pybind11_protobuf
pybind11_abseil
boringssl
org_brotli
hwloc
Pass each directory with --override_repository=name=/absolute/source/path.
Use empty local repository stubs for unsupported prebuilt x86/AArch64 PyPI
repositories such as grpcio, optree, h5py, SciPy, libclang, and TensorBoard's
data server. This keeps foreign wheels out of the LoongArch build graph.
The consolidated TensorFlow patch does not embed these third-party source trees. Preserve their licenses and source revision records in any distributed build kit.
Build a target-matched static C++ runtime
TensorFlow's roughly 2 GiB shared library exceeds the LoongArch normal code
model. Rebuild a PIC, medium-model libstdc++.a from GCC 15.2 source using the
same CPU flags chosen above:
gcc_root=/opt/loongson-gcc-15.2.0
source_root=/path/to/gcc-15.2.0
build_root=/path/to/libstdcxx-build
prefix=/path/to/target-libstdcxx
cpu_flags='-march=la464 -mtune=la464 -mlsx -mno-lasx' # choose from the table
common_flags="-O2 -g0 -fPIC -mcmodel=medium $cpu_flags"
bits_include="$gcc_root/include/c++/15.2.0/loongarch64-linux-gnu/bits"
mkdir -p "$build_root"
cd "$build_root"
CC="$gcc_root/bin/gcc" \
CXX="$gcc_root/bin/g++" \
CFLAGS="$common_flags" \
CXXFLAGS="$common_flags -I$bits_include" \
"$source_root/libstdc++-v3/configure" \
--prefix="$prefix" \
--build=loongarch64-linux-gnu \
--host=loongarch64-linux-gnu \
--disable-multilib \
--disable-shared \
--enable-static \
--disable-libstdcxx-pch \
--enable-libstdcxx-threads
make -j4
make install
test -f "$prefix/lib/libstdc++.a"
Use this archive for target and host links with -static-libstdc++. Keep
libgcc_s.so.1 dynamic; forcing the installed libgcc_eh.a into the large
TensorFlow link can reintroduce normal-model relocation overflows.
For the LA464/LSX profile, disassemble the archive and reject any LASX instruction before continuing. For the LASX profiles, verify that representative LASX instructions are present and execute correctly on a compatible host.
Build the native dependency wheels
Create a clean CPython 3.13.13 virtual environment for the selected profile.
Export the chosen flags through CFLAGS, CXXFLAGS, and the build-system
specific options for every native project:
export CFLAGS="-O2 -g0 -fPIC -mcmodel=medium $cpu_flags"
export CXXFLAGS="$CFLAGS"
Build the standard NumPy, HDF5/h5py, ml_dtypes, optree, and grpcio wheels before TensorFlow. Build the experimental NumPy separately if desired.
NumPy's Meson option remains -Dcpu-baseline=LSX for all three profiles because
NumPy exposes LSX, not LASX, as its named LoongArch baseline. The global compiler
flags still control whether LASX code may be emitted. The 3A5000/3B5000 build
must retain -mno-lasx.
When statically linking the private C++ runtime into ml_dtypes, use:
-Wl,--exclude-libs,libstdc++.a
This prevents private static C++ symbols from interposing on TensorFlow, grpcio, optree, or another extension when import order changes.
Configure and build TensorFlow
Use GCC 15.2 for both target and host actions. The common Bazel configuration used by the reference build includes:
--enable_workspace
--noenable_bzlmod
--config=opt
--config=nogcp
--config=nonccl
--config=clang_local
--define=tflite_with_ruy=true
--define=tensorflow_mkldnn_contraction_kernel=0
--platforms=//:linux_loongarch64
--host_platform=//:linux_loongarch64
--repo_env=TF_DOWNLOAD_CLANG=0
--repo_env=USE_PYWRAP_RULES=1
--repo_env=WHEEL_NAME=tensorflow
--local_resources=cpu=4
--local_resources=memory=10000
--jobs=4
For every flag in the selected CPU profile, pass all four forms:
--copt=FLAG
--cxxopt=FLAG
--host_copt=FLAG
--host_cxxopt=FLAG
Also pass -mcmodel=medium in all four forms. Put the target-matched static
runtime first in the search path and link it for both target and host:
--linkopt=-L/path/to/target-libstdcxx/lib
--host_linkopt=-L/path/to/target-libstdcxx/lib
--linkopt=/path/to/target-libstdcxx/lib/libstdc++.a
--host_linkopt=/path/to/target-libstdcxx/lib/libstdc++.a
--linkopt=-static-libstdc++
--host_linkopt=-static-libstdc++
Run an analysis-only pass first by adding --nobuild, then compile the wheel:
bazel --output_base="$HOME/.cache/bazel/tf-2.21.0-TARGET" \
build //tensorflow/tools/pip_package:wheel \
"${bazel_args[@]}" --nobuild
bazel --output_base="$HOME/.cache/bazel/tf-2.21.0-TARGET" \
build //tensorflow/tools/pip_package:wheel \
"${bazel_args[@]}"
Here bazel_args contains the common settings, hermetic Python settings,
repository overrides, compiler environment, selected CPU flags, and link flags
described above. Replace TARGET with la464-lsx, la464-lasx, or
la664-lasx; never share an output base between profiles.
Validate and package the result
Test the wheel in a new CPython 3.13.14 environment. At minimum, verify:
- the
linux_loongarch64wheel tag and LoongArch ELF machine type; - old-world
0x3, LP64ELF flags; - no dynamic
libstdc++.sodependency; - no GLIBC symbol newer than
GLIBC_2.28; - no LASX instructions anywhere in the LA464/LSX wheel or its native dependencies;
- LASX instructions in the LA464/LASX and LA664/LASX builds;
pip checkafter installing the complete wheelhouse;- imports of TensorFlow and every native dependency;
- NumPy arithmetic, HDF5 I/O, and a basic gRPC operation;
- TensorFlow dense matrix multiplication, BF16 sparse matrix multiplication, and float32 2D and 3D convolutions.
Package TensorFlow together with the dependency wheels built under the same CPU profile. Do not combine a TensorFlow wheel from one profile with native dependency wheels from another. Record SHA-256 checksums, source revisions, third-party licenses, and the selected compiler flags with the release.
Common failure modes
- Illegal instruction on a 3A5000: at least one native component was built
without
-mno-lasx, or an LA464/LASX or LA664 wheel entered the environment. - Relocation overflow during the TensorFlow link:
-mcmodel=mediumwas not applied everywhere, or the normal-model compilerlibstdc++.awas selected. - Bazel reuses the wrong ISA: the output base or
.tf_configure.bazelrccame from another CPU profile. Start from a clean profile-specific tree. - Foreign wheel appears in the graph: replace the unsupported PyPI repository with a local empty stub and build that dependency from source.
- Import-order crash: make sure private static C++ symbols are hidden, especially in ml_dtypes.
