Getting Started on Android with CMake

This guide walks through cross-compiling IREE core runtime towards the Android platform. Cross-compiling IREE compilers towards Android is not supported at the moment.

Cross-compilation involves both a host platform and a target platform. One invokes compiler toolchains on the host platform to generate libraries and executables that can be run on the target platform.

Prerequisites

Set up host development environment

The host platform should have been set up for developing IREE. Right now Linux and Windows are supported. Please make sure you have followed the steps for Linux or Windows.

Install Android NDK

Android NDK provides compiler toolchains for compiling C/C++ code to target Android. You can download it here. We recommend to download the latest release; the steps in following sections may assume that.

Alternatively, if you have installed Android Studio, you can follow this guide to install Android NDK.

After downloading, it is recommended to set the ANDROID_NDK environment variable pointing to the directory. For Linux, you can export in your shell's rc file. For Windows, you can search “environment variable” in the taskbar or use Windows + R to open the “Run” dialog to run rundll32 sysdm.cpl,EditEnvironmentVariables.

Install Android Debug Bridge (ADB)

For Linux, search your the distribution's package manager to install adb. For example, on Ubuntu:

$ sudo apt install adb

For Windows, it's easier to get adb via Android Studio. adb is included in the Android SDK Platform-Tools package. You can download this package with the SDK Manager, which installs it at android_sdk/platform-tools/. Or if you want the standalone Android SDK Platform-Tools package, you can download it here. You may also want to add the folder to the PATH environment variable.

Configure and build

Host configuration

Build and install at least the compiler tools on your host machine, or install them from a binary distribution:

$ cmake -G Ninja -B ../iree-build-host/ -DCMAKE_INSTALL_PREFIX=../iree-build-host/install .
$ cmake --build ../iree-build-host/ --target install

Debugging note:

  • If IREE_LLVMAOT_LINKER_PATH is set for targeting Android then the build above will fail, and you should run unset IREE_LLVMAOT_LINKER_PATH.
  • If you experience the build error similar to issue #4915, update the cmake configuration CLI to
$ cmake -G Ninja -B ../iree-build-host/ \
  -DCMAKE_C_COMPILER=clang \
  -DCMAKE_CXX_COMPILER=clang++ \
  -DCMAKE_INSTALL_PREFIX=../iree-build-host/install .

Target configuration

Build the runtime using the Android NDK toolchain:

$ cmake -G Ninja -B ../iree-build-android/ \
  -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK?}/build/cmake/android.toolchain.cmake" \
  -DIREE_HOST_BINARY_ROOT=$(realpath ../iree-build-host/install) \
  -DANDROID_ABI="arm64-v8a" \
  -DANDROID_PLATFORM=android-29 \
  -DIREE_BUILD_COMPILER=OFF \
  -DIREE_BUILD_SAMPLES=OFF \
  .
$ cmake --build ../iree-build-android/
  • The above configures IREE to cross-compile towards 64-bit (-DANDROID_ABI="arm64-v8a") Android 10 (-DANDROID_PLATFORM=android-29). This may require the latest Android NDK release. You can choose the suitable ANDROID_ABI and ANDROID_PLATFORM for your target device. You can also refer to Android NDK's CMake documentation for more toolchain arguments.

Test on Android

Make sure you enable developer options and USB debugging for your Android device.

Connect your Android device to the development machine and make sure you can see the device when:

$ adb devices

List of devices attached
XXXXXXXXXXX     device

Then you can run all device tests via

$ cd ../iree-build-android
$ ctest --output-on-failure

The above command will upload necessary build artifacts to the Android device's /data/local/tmp directory, run the tests there, and report status back.

Alternatively, if you want to invoke a specific HAL backend on a IREE module:

VMLA HAL backend

Translate a source MLIR into IREE module:

# Assuming in IREE source root
$ ../iree-build-host/install/bin/iree-translate \
  -iree-mlir-to-vm-bytecode-module \
  -iree-hal-target-backends=vmvx \
  $PWD/iree/samples/models/simple_abs.mlir \
  -o /tmp/simple_abs_vmvx.vmfb

Then push the IREE runtime executable and module to the device:

$ adb push ../iree-build-android/iree/tools/iree-run-module /data/local/tmp/
$ adb shell chmod +x /data/local/tmp/iree-run-module
$ adb push /tmp/simple_abs_vmvx.vmfb /data/local/tmp/

Log into Android:

$ adb shell

android $ cd /data/local/tmp/
android $ ./iree-run-module \
          --driver=vmvx \
          --module_file=simple_abs_vmvx.vmfb \
          --entry_function=abs \
          --function_input=f32=-5

EXEC @abs
f32=5

Vulkan HAL backend

Please make sure your Android device is Vulkan capable. Vulkan is supported on Android since 7, but Android 10 is our primary target at the moment.

Translate a source MLIR into IREE module:

$ ../iree-build-host/install/bin/iree-translate \
    -iree-mlir-to-vm-bytecode-module \
    -iree-hal-target-backends=vulkan-spirv \
    $PWD/iree/samples/models/simple_abs.mlir \
    -o /tmp/simple_abs_vulkan.vmfb

Then push the IREE runtime executable and module to the device:

$ adb push ../iree-build-android/iree/tools/iree-run-module /data/local/tmp/
$ adb shell chmod +x /data/local/tmp/iree-run-module
$ adb push /tmp/simple_abs_vulkan.vmfb /data/local/tmp/

Log into Android:

$ adb shell

android $ cd /data/local/tmp/
android $ ./iree-run-module \
          --driver=vulkan \
          --module_file=simple_abs_vulkan.vmfb \
          --entry_function=abs \
          --function_input=f32=-5

EXEC @abs
f32=5

Common issues

Vulkan function vkCreateInstance not available

Since Android 8 Oreo, Android re-architected the OS framework with project Treble. Framework libraries and vendor libraries have a more strict and clear separation. Their dependencies are carefully scrutinized and only selected cases are allowed. This is enforced with linker namespaces.

/data/local/tmp is the preferred directory for automating native binary tests built using NDK toolchain. They should be allowed to access libraries like libvulkan.so for their functionality. However, there was an issue with fully treblized Android 10 where /data/local/tmp did not have access to the linker namespaces needed by libvulkan.so. This should be fixed now. But as typically in the Android system, it takes a long time to see the fix getting propagated, if ever.

A known workaround is to symlink the vendor Vulkan implementation under /vendor/lib[64] as libvulkan.so under /data/local/tmp and use LD_LIBRARY_PATH=/data/local/tmp when invoking IREE executables.

For Qualcomm Adreno GPUs, the vendor Vulkan implementation is at /vendor/lib[64]/hw/vulkan.*.so. So for example for Snapdragon 865:

$ adb shell ln -s /vendor/lib64/hw/vulkan.kona.so /data/local/tmp/libvulkan.so

For ARM Mali GPUs, there is only one monolithic driver (/vendor/lib[64]/libGLES_mali.so) for OpenGL and Vulkan and the Vulkan vendor driver (/vendor/lib[64]/hw/vulkan.*.so) is just a symlink to it. So for example:

$ adb shell ln -s /vendor/lib64/libGLES_mali.so /data/local/tmp/libvulkan.so

Dylib LLVM AOT backend

Translate a source MLIR into an IREE module:

$ ../iree-build-host/install/bin/iree-translate \
  -iree-mlir-to-vm-bytecode-module \
  -iree-hal-target-backends=dylib-llvm-aot \
  -iree-llvm-target-triple=aarch64-linux-android \
  $PWD/iree/samples/models/simple_abs.mlir \
  -o /tmp/simple_abs_dylib.vmfb

Then push the IREE runtime executable and module to the device:

$ adb push ../iree-build-android/iree/tools/iree-run-module /data/local/tmp/
$ adb shell chmod +x /data/local/tmp/iree-run-module
$ adb push /tmp/simple_abs_dylib.vmfb /data/local/tmp/

Log into Android:

$ adb shell

android $ cd /data/local/tmp/
android $ ./iree-run-module \
          --driver=dylib \
          --module_file=simple_abs_dylib.vmfb \
          --entry_function=abs \
          --function_input=f32=-5

EXEC @abs
f32=5