Skip to content

[llvm][docs] Update CMake commands for cross compiling Arm builtins #151544

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
242 changes: 142 additions & 100 deletions llvm/docs/HowToCrossCompileBuiltinsOnArm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ using as many of the LLVM tools as we can, but it is possible to use GNU
equivalents.

You will need:
* A build of LLVM for the llvm-tools and ``llvm-config``.
* A build of LLVM for the llvm-tools and LLVM CMake files.
* A clang executable with support for the ``ARM`` target.
* compiler-rt sources.
* ``compiler-rt`` sources.
* The ``qemu-arm`` user mode emulator.
* An ``arm-linux-gnueabihf`` sysroot.

.. note::
An existing sysroot is required because some of the builtins include C library
headers and a sysroot is the easiest way to get those.

In this example we will be using ``ninja`` as the build tool.

See https://compiler-rt.llvm.org/ for information about the dependencies
Expand All @@ -52,78 +56,94 @@ toolchain from https://developer.arm.com/open-source/gnu-toolchain/gnu-a/downloa
Building compiler-rt builtins for Arm
=====================================

We will be doing a standalone build of compiler-rt using the following cmake
options::
We will be doing a standalone build of compiler-rt. The command is shown below.
Shell variables are used to simplify some of the options::

LLVM_TOOLCHAIN=<path-to-llvm-install>/
TARGET_TRIPLE=arm-none-linux-gnueabihf
GCC_TOOLCHAIN=<path-to-gcc-toolchain>
SYSROOT=${GCC_TOOLCHAIN}/${TARGET_TRIPLE}/libc
COMPILE_FLAGS="-march=armv7-a"

cmake path/to/compiler-rt \
cmake ../llvm-project/compiler-rt \
-G Ninja \
-DCMAKE_AR=/path/to/llvm-ar \
-DCMAKE_ASM_COMPILER_TARGET="arm-linux-gnueabihf" \
-DCMAKE_ASM_FLAGS="build-c-flags" \
-DCMAKE_C_COMPILER=/path/to/clang \
-DCMAKE_C_COMPILER_TARGET="arm-linux-gnueabihf" \
-DCMAKE_C_FLAGS="build-c-flags" \
-DCMAKE_AR=${LLVM_TOOLCHAIN}/bin/llvm-ar \
-DCMAKE_NM=${LLVM_TOOLCHAIN}/bin/llvm-nm \
-DCMAKE_RANLIB=${LLVM_TOOLCHAIN}/bin/llvm-ranlib \
-DLLVM_CMAKE_DIR="${LLVM_TOOLCHAIN}/lib/cmake/llvm" \
-DCMAKE_SYSROOT="${SYSROOT}" \
-DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_ASM_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \
-DCMAKE_C_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCMAKE_C_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \
-DCMAKE_CXX_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCMAKE_CXX_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \
-DCMAKE_NM=/path/to/llvm-nm \
-DCMAKE_RANLIB=/path/to/llvm-ranlib \
-DCOMPILER_RT_BUILD_BUILTINS=ON \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCOMPILER_RT_BUILD_MEMPROF=OFF \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
-DCOMPILER_RT_BUILD_XRAY=OFF \
-DCOMPILER_RT_BUILD_ORC=OFF \
-DCOMPILER_RT_BUILD_CRT=OFF \
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
-DLLVM_CONFIG_PATH=/path/to/llvm-config
-DCOMPILER_RT_EMULATOR="qemu-arm -L ${SYSROOT}" \
-DCOMPILER_RT_INCLUDE_TESTS=ON \
-DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${TARGET_TRIPLE} ${COMPILE_FLAGS} --gcc-toolchain=${GCC_TOOLCHAIN} --sysroot=${SYSROOT} -fuse-ld=lld"

The ``build-c-flags`` need to be sufficient to pass the C-make compiler check,
compile compiler-rt, and if you are running the tests, compile and link the
tests. When cross-compiling with clang we will need to pass sufficient
information to generate code for the Arm architecture we are targeting.
.. note::
The command above also enables tests. Enabling tests is not required, more details
in the testing section.

We will need to select:
* The Arm target and Armv7-A architecture with ``--target=arm-linux-gnueabihf -march=armv7a``.
* Whether to generate Arm (the default) or Thumb instructions (``-mthumb``).
``CMAKE_<LANGUAGE>_<OPTION>`` options are set so that the correct ``--target``,
``--sysroot``, ``--gcc-toolchain`` and ``-march`` options will be given to the
compilers.

When using a GCC ``arm-linux-gnueabihf`` toolchain the following flags are
needed to pick up the includes and libraries:
The combination of these settings needs to be enough to pass CMake's compiler
checks, compile compiler-rt and build the test cases.

* ``--gcc-toolchain=/path/to/dir/toolchain``
* ``--sysroot=/path/to/toolchain/arm-linux-gnueabihf/libc``
The flags need to select:
* The Arm target (``--target arm-none-linux-gnueabihf``)
* The Arm architecture level (``-march=armv7-a``)
* Whether to generate Arm (``-marm``, the default) or Thumb (``-mthumb``) instructions.

In this example we will be adding all of the command line options to both
``CMAKE_C_FLAGS`` and ``CMAKE_ASM_FLAGS``. There are cmake flags to pass some of
these options individually which can be used to simplify the ``build-c-flags``::
It is possible to pass all these flags to CMake using ``CMAKE_<LANGUAGE>_FLAGS``,
but the command above uses standard CMake options instead. If you need to
add flags that CMake cannot generate automatically, add them to
``CMAKE_<LANGUAGE>_FLAGS``.

-DCMAKE_C_COMPILER_TARGET="arm-linux-gnueabihf"
-DCMAKE_ASM_COMPILER_TARGET="arm-linux-gnueabihf"
-DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=/path/to/dir/toolchain
-DCMAKE_SYSROOT=/path/to/dir/toolchain/arm-linux-gnueabihf/libc
When CMake has finished, build with Ninja::

Once cmake has completed the builtins can be built with ``ninja builtins``
ninja builtins

Testing compiler-rt builtins using qemu-arm
===========================================

To test the builtins library we need to add a few more cmake flags to enable
testing and set up the compiler and flags for test case. We must also tell
cmake that we wish to run the tests on ``qemu-arm``::
The following options are required to enable tests::

-DCOMPILER_RT_EMULATOR="qemu-arm -L ${SYSROOT}" \
-DCOMPILER_RT_INCLUDE_TESTS=ON \
-DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${TARGET_TRIPLE} -march=armv7-a --gcc-toolchain=${GCC_TOOLCHAIN} --sysroot=${SYSROOT} -fuse-ld=lld"

-DCOMPILER_RT_EMULATOR="qemu-arm -L /path/to/armhf/sysroot"
-DCOMPILER_RT_INCLUDE_TESTS=ON
-DCOMPILER_RT_TEST_COMPILER="/path/to/clang"
-DCOMPILER_RT_TEST_COMPILER_CFLAGS="test-c-flags"
This tells compiler-rt that we want to run tests on ``qemu-arm``. If you do not
want to run tests, remove these options from the CMake command.

The ``/path/to/armhf/sysroot`` should be the same as the one passed to
``--sysroot`` in the ``build-c-flags``.
Note that ``COMPILER_RT_TEST_COMPILER_CFLAGS`` contains the equivalent of the
options CMake generated for us with the first command. We must pass them
manually here because standard options like ``CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN``
do not apply here.

The ``test-c-flags`` need to include the target, architecture, gcc-toolchain,
sysroot and Arm/Thumb state. The additional cmake defines such as
``CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN`` do not apply when building the tests. If
you have put all of these in ``build-c-flags`` then these can be repeated. If you
wish to use lld to link the tests then add ``-fuse-ld=lld``.
When CMake has finished, run the tests::

Once cmake has completed the tests can be built and run using
``ninja check-builtins``
ninja check-builtins

Troubleshooting
===============
Expand All @@ -133,9 +153,10 @@ The cmake try compile stage fails
At an early stage cmake will attempt to compile and link a simple C program to
test if the toolchain is working.

This stage can often fail at link time if the ``--sysroot=`` and
This stage can often fail at link time if the ``--sysroot=``, ``--target`` or
``--gcc-toolchain=`` options are not passed to the compiler. Check the
``CMAKE_C_FLAGS`` and ``CMAKE_C_COMPILER_TARGET`` flags.
``CMAKE_<LANGUAGE>_FLAGS`` and ``CMAKE_<LANGAUGE>_COMPILER_TARGET`` flags along
with any of the specific CMake sysroot and toolchain options.

It can be useful to build a simple example outside of cmake with your toolchain
to make sure it is working. For example::
Expand Down Expand Up @@ -179,10 +200,10 @@ The flags used to build the tests are not the same as those used to build the
builtins. The c flags are provided by ``COMPILER_RT_TEST_COMPILE_CFLAGS`` and
the ``CMAKE_C_COMPILER_TARGET``, ``CMAKE_ASM_COMPILER_TARGET``,
``CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN`` and ``CMAKE_SYSROOT`` flags are not
applied.
applied to tests.

Make sure that ``COMPILER_RT_TEST_COMPILE_CFLAGS`` contains all the necessary
information.
flags.


Modifications for other Targets
Expand All @@ -206,13 +227,13 @@ You will need to use an ``arm-linux-gnueabi`` GNU toolchain for soft-float.
AArch64 Target
--------------
The instructions for Arm can be used for AArch64 by substituting AArch64
equivalents for the sysroot, emulator and target.
equivalents for the sysroot, emulator and target::

* ``-DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu``
* ``-DCOMPILER_RT_EMULATOR="qemu-aarch64 -L /path/to/aarch64/sysroot``
-DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu
-DCOMPILER_RT_EMULATOR="qemu-aarch64 -L /path/to/aarch64/sysroot

The CMAKE_C_FLAGS and COMPILER_RT_TEST_COMPILER_CFLAGS may also need:
``"--sysroot=/path/to/aarch64/sysroot --gcc-toolchain=/path/to/gcc-toolchain"``
You will also have to update any use of the target triple in compiler flags.
For instance in ``CMAKE_C_FLAGS`` and ``COMPILER_RT_TEST_COMPILER_CFLAGS``.

Armv6-m, Armv7-m and Armv7E-M targets
-------------------------------------
Expand All @@ -221,7 +242,7 @@ but more difficult. The main problems are:

* There is not a ``qemu-arm`` user-mode emulator for bare-metal systems.
``qemu-system-arm`` can be used but this is significantly more difficult
to setup.
to setup. This document does not explain how to do this.
* The targets to compile compiler-rt have the suffix ``-none-eabi``. This uses
the BareMetal driver in clang and by default will not find the libraries
needed to pass the cmake compiler check.
Expand All @@ -235,31 +256,68 @@ into a binary and execute the tests correctly but it will not catch if the
builtins use instructions that are supported on Armv7-A but not Armv6-M,
Armv7-M and Armv7E-M.

To get the cmake compile test to pass you will need to pass the libraries
needed to successfully link the cmake test via ``CMAKE_CFLAGS``::

-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
-DCOMPILER_RT_OS_DIR="baremetal" \
-DCOMPILER_RT_BUILD_BUILTINS=ON \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
-DCOMPILER_RT_BUILD_XRAY=OFF \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCMAKE_C_COMPILER=${host_install_dir}/bin/clang \
-DCMAKE_C_COMPILER_TARGET="your *-none-eabi target" \
-DCMAKE_ASM_COMPILER_TARGET="your *-none-eabi target" \
-DCMAKE_AR=/path/to/llvm-ar \
-DCMAKE_NM=/path/to/llvm-nm \
-DCMAKE_RANLIB=/path/to/llvm-ranlib \
-DCOMPILER_RT_BAREMETAL_BUILD=ON \
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
-DLLVM_CONFIG_PATH=/path/to/llvm-config \
-DCMAKE_C_FLAGS="build-c-flags" \
-DCMAKE_ASM_FLAGS="build-c-flags" \
-DCOMPILER_RT_EMULATOR="qemu-arm -L /path/to/armv7-A/sysroot" \
-DCOMPILER_RT_INCLUDE_TESTS=ON \
-DCOMPILER_RT_TEST_COMPILER="/path/to/clang" \
-DCOMPILER_RT_TEST_COMPILER_CFLAGS="test-c-flags"
This requires a second ``arm-none-eabi`` toolchain for building the builtins.
Using a bare-metal toolchain ensures that the target and C library details are
specific to bare-metal instead of using Linux settings. This means that some
tests may behave differently compared to real hardware, but at least the content
of the builtins library is correct.

Below is an example that builds the builtins for Armv7-M, but runs the tests
as Armv7-A. It is presented in full, but is very similar to the earlier
command for Armv7-A build and test::

LLVM_TOOLCHAIN=<path to llvm install>/

# For the builtins.
TARGET_TRIPLE=arm-none-eabi
GCC_TOOLCHAIN=<path to arm-none-eabi toolchain>/
SYSROOT=${GCC_TOOLCHAIN}/${TARGET_TRIPLE}/libc
COMPILE_FLAGS="-march=armv7-m -mfpu=vfpv2"

# For the test cases.
A_PROFILE_TARGET_TRIPLE=arm-none-linux-gnueabihf
A_PROFILE_GCC_TOOLCHAIN=<path to arm-none-linux-gnueabihf toolchain>/
A_PROFILE_SYSROOT=${A_PROFILE_GCC_TOOLCHAIN}/${A_PROFILE_TARGET_TRIPLE}/libc

cmake ../llvm-project/compiler-rt \
-G Ninja \
-DCMAKE_AR=${LLVM_TOOLCHAIN}/bin/llvm-ar \
-DCMAKE_NM=${LLVM_TOOLCHAIN}/bin/llvm-nm \
-DCMAKE_RANLIB=${LLVM_TOOLCHAIN}/bin/llvm-ranlib \
-DLLVM_CMAKE_DIR="${LLVM_TOOLCHAIN}/lib/cmake/llvm" \
-DCMAKE_SYSROOT="${SYSROOT}" \
-DCMAKE_ASM_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_ASM_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_C_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \
-DCMAKE_C_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCMAKE_C_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_CXX_COMPILER_TARGET="${TARGET_TRIPLE}" \
-DCMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=${GCC_TOOLCHAIN} \
-DCMAKE_CXX_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCMAKE_CXX_FLAGS="${COMPILE_FLAGS}" \
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \
-DCOMPILER_RT_BUILD_BUILTINS=ON \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCOMPILER_RT_BUILD_MEMPROF=OFF \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCOMPILER_RT_BUILD_CTX_PROFILE=OFF \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
-DCOMPILER_RT_BUILD_XRAY=OFF \
-DCOMPILER_RT_BUILD_ORC=OFF \
-DCOMPILER_RT_BUILD_CRT=OFF \
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
-DCOMPILER_RT_EMULATOR="qemu-arm -L ${A_PROFILE_SYSROOT}" \
-DCOMPILER_RT_INCLUDE_TESTS=ON \
-DCOMPILER_RT_TEST_COMPILER=${LLVM_TOOLCHAIN}/bin/clang \
-DCOMPILER_RT_TEST_COMPILER_CFLAGS="--target=${A_PROFILE_TARGET_TRIPLE} -march=armv7-a --gcc-toolchain=${A_PROFILE_GCC_TOOLCHAIN} --sysroot=${A_PROFILE_SYSROOT} -fuse-ld=lld" \
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY \
-DCOMPILER_RT_OS_DIR="baremetal" \
-DCOMPILER_RT_BAREMETAL_BUILD=ON

.. note::
The sysroot used for compiling the tests is ``arm-linux-gnueabihf``, not
``arm-none-eabi`` which is used when compiling the builtins.

The Armv6-M builtins will use the soft-float ABI. When compiling the tests for
Armv7-A we must include ``"-mthumb -mfloat-abi=soft -mfpu=none"`` in the
Expand All @@ -270,19 +328,3 @@ mismatches between the M-profile objects from compiler-rt and the A-profile
objects from the test. The lld linker does not check the profile
BuildAttribute so it can be used to link the tests by adding ``-fuse-ld=lld`` to the
``COMPILER_RT_TEST_COMPILER_CFLAGS``.

Alternative using a cmake cache
-------------------------------
If you wish to build, but not test compiler-rt for Armv6-M, Armv7-M or Armv7E-M
the easiest way is to use the ``BaremetalARM.cmake`` recipe in ``clang/cmake/caches``.

You will need a bare metal sysroot such as that provided by the GNU ARM Embedded
toolchain.

The libraries can be built with the cmake options::

-DBAREMETAL_ARMV6M_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi \
-DBAREMETAL_ARMV7M_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi \
-DBAREMETAL_ARMV7EM_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi \
-C /path/to/llvm/source/tools/clang/cmake/caches/BaremetalARM.cmake \
/path/to/llvm
Loading