Skip to content
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

Enable RISC-V 64-bit Cross-Compiling Support for ONNX Runtime on Linux #19238

Merged
merged 4 commits into from
Jan 25, 2024
Merged
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
6 changes: 5 additions & 1 deletion cmake/external/xnnpack.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ set(FP16_BUILD_BENCHMARKS OFF CACHE INTERNAL "")
set(PTHREADPOOL_BUILD_TESTS OFF CACHE INTERNAL "")
set(PTHREADPOOL_BUILD_BENCHMARKS OFF CACHE INTERNAL "")

if(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv64.*")
set(XNNPACK_USE_SYSTEM_LIBS OFF)
endif()

# BF16 instructions cause ICE in Android NDK compiler
if(CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a)
set(XNNPACK_ENABLE_ARM_BF16 OFF)
ENDIF()
endif()

# fp16 depends on psimd
FetchContent_Declare(psimd URL ${DEP_URL_psimd} URL_HASH SHA1=${DEP_SHA1_psimd})
Expand Down
4 changes: 3 additions & 1 deletion cmake/onnxruntime_common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
set(ARM TRUE)
elseif(dumpmachine_output MATCHES "^aarch64.*")
set(ARM64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv64.*")
set(RISCV64 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(i.86|x86?)$")
set(X86 TRUE)
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|amd64)$")
Expand All @@ -198,7 +200,7 @@ elseif(NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
endif()


if (ARM64 OR ARM OR X86 OR X64 OR X86_64)
if (RISCV64 OR ARM64 OR ARM OR X86 OR X64 OR X86_64)
if((WIN32 AND NOT CMAKE_CXX_STANDARD_LIBRARIES MATCHES kernel32.lib) OR ((ARM64 OR ARM) AND MSVC))
# msvc compiler report syntax error with cpuinfo arm source files
# and cpuinfo does not have code for getting arm uarch info under windows
Expand Down
35 changes: 35 additions & 0 deletions cmake/riscv64.toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2024 SiFive, Inc. All rights reserved.
# Copyright (c) 2024, Phoebe Chen <phoebe.chen@sifive.com>
# Licensed under the MIT License.

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)

list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES RISCV_TOOLCHAIN_ROOT)

if(NOT RISCV_TOOLCHAIN_ROOT)
message(FATAL_ERROR "RISCV_TOOLCHAIN_ROOT is not defined. Please set the RISCV_TOOLCHAIN_ROOT variable.")
endif()

set(CMAKE_C_COMPILER "${RISCV_TOOLCHAIN_ROOT}/bin/riscv64-unknown-linux-gnu-gcc")
set(CMAKE_ASM_COMPILER "${RISCV_TOOLCHAIN_ROOT}/bin/riscv64-unknown-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${RISCV_TOOLCHAIN_ROOT}/bin/riscv64-unknown-linux-gnu-g++")

set(CMAKE_FIND_ROOT_PATH ${RISCV_TOOLCHAIN_ROOT})
set(CMAKE_SYSROOT "${RISCV_TOOLCHAIN_ROOT}/sysroot")
set(CMAKE_INCLUDE_PATH "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/include/")
set(CMAKE_LIBRARY_PATH "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/lib/")
set(CMAKE_PROGRAM_PATH "${RISCV_TOOLCHAIN_ROOT}/sysroot/usr/bin/")

if(RISCV_QEMU_PATH)
message(STATUS "RISCV_QEMU_PATH=${RISCV_QEMU_PATH} is defined during compilation.")
set(CMAKE_CROSSCOMPILING_EMULATOR "${RISCV_QEMU_PATH};-L;${CMAKE_SYSROOT}")
endif()

set(CMAKE_CROSSCOMPILING TRUE)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

35 changes: 34 additions & 1 deletion tools/ci_build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,12 @@ def convert_arg_line_to_args(self, arg_line):
help="[cross-compiling] Create Windows x86 makefiles. Requires --update and no existing cache "
"CMake setup. Delete CMakeCache.txt if needed",
)
parser.add_argument(
"--rv64",
action="store_true",
help="[cross-compiling] Create riscv64 makefiles. Requires --update and no existing cache "
"CMake setup. Delete CMakeCache.txt if needed",
)
parser.add_argument(
"--arm",
action="store_true",
Expand All @@ -351,6 +357,18 @@ def convert_arg_line_to_args(self, arg_line):
action="store_true",
help="[cross-compiling] Create ARM64X Binary.",
)
parser.add_argument(
"--riscv_toolchain_root",
type=str,
default="",
help="Path to RISC-V toolchain root dir. e.g. --riscv_toolchain_root=$HOME/riscv-tools/",
)
parser.add_argument(
"--riscv_qemu_path",
type=str,
default="",
help="Path to RISC-V qemu. e.g. --riscv_qemu_path=$HOME/qemu-dir/qemu-riscv64",
)
parser.add_argument("--msvc_toolset", help="MSVC toolset to use. e.g. 14.11")
parser.add_argument("--windows_sdk_version", help="Windows SDK version to use. e.g. 10.0.19041.0")
parser.add_argument("--android", action="store_true", help="Build for Android")
Expand Down Expand Up @@ -1077,6 +1095,19 @@ def generate_build_tree(
"-Donnxruntime_DISABLE_OPTIONAL_TYPE=" + ("ON" if disable_optional_type else "OFF"),
]

if args.rv64:
add_default_definition(cmake_extra_defines, "onnxruntime_CROSS_COMPILING", "ON")
if not args.riscv_toolchain_root:
raise BuildError("The --riscv_toolchain_root option is required to build for riscv64.")
if not args.skip_tests and not args.riscv_qemu_path:
raise BuildError("The --riscv_qemu_path option is required for testing riscv64.")

cmake_args += [
"-DRISCV_TOOLCHAIN_ROOT:PATH=" + args.riscv_toolchain_root,
"-DRISCV_QEMU_PATH:PATH=" + args.riscv_qemu_path,
"-DCMAKE_TOOLCHAIN_FILE=" + os.path.join(source_dir, "cmake", "riscv64.toolchain.cmake"),
]

# By default on Windows we currently support only cross compiling for ARM/ARM64
# (no native compilation supported through this script).
if args.arm64 or args.arm64ec or args.arm:
Expand Down Expand Up @@ -1553,7 +1584,9 @@ def generate_build_tree(
]
if is_linux() and platform.machine() == "x86_64":
# The following flags needs GCC 8 and newer
cflags += ["-fstack-clash-protection", "-fcf-protection"]
cflags += ["-fstack-clash-protection"]
if not args.rv64:
cflags += ["-fcf-protection"]
cxxflags = cflags.copy()
if args.use_cuda:
cudaflags = cflags.copy()
Expand Down
129 changes: 129 additions & 0 deletions tools/scripts/build_riscv64.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/bin/bash
# Copyright (c) 2024 SiFive, Inc. All rights reserved.
# Copyright (c) 2024, Phoebe Chen <phoebe.chen@sifive.com>
# Licensed under the MIT License.


# The script is a sample for RISC-V 64-bit cross compilation in
# GNU/Linux, and you should ensure that your environment meets
# ORT requirements. You may need to make changes before using it.

set -e
set -o pipefail

# Get directory this script is in
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
OS=$(uname -s)

if [ "$OS" == "Linux" ]; then
LINUX_DISTRO=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"')
if [[ "${LINUX_DISTRO}" == "ubuntu" ]] ;then
DIR_OS="Linux"
else
echo "${LINUX_DISTRO} is not supported"
return 1
fi
else
echo "$OS is not supported"
return 1
fi

function cleanup {
if [ -d "$WORK_DIR" ]; then
rm -rf "$WORK_DIR"
fi
}

# The riscv toolchain, qemu and other platform related settings.
ORT_ROOT_DIR=$DIR/../..

PREBUILT_DIR="${ORT_ROOT_DIR}/riscv_tools"

read -rp "Enter the riscv tools root path(press enter to use default path:${PREBUILT_DIR}): " INPUT_PATH
if [[ "${INPUT_PATH}" ]]; then
PREBUILT_DIR=${INPUT_PATH}
fi
echo "The riscv tool prefix path: ${PREBUILT_DIR}"

WORK_DIR=$DIR/.prebuilt

# The prebuit toolchain download from riscv-collab works with Ubuntu.
RISCV_GNU_TOOLCHAIN_URL="https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download"
TOOLCHAIN_VERSION="2023.11.20"
RISCV_TOOLCHAIN_FILE_NAME="riscv64-glibc-ubuntu-22.04-llvm-nightly-2023.11.20-nightly.tar.gz"
RISCV_TOOLCHAIN_FILE_SHA="98d6531b757fac01e065460c19abe8974976c607a8d88631cc5c1529d90ba7ba"

TOOLCHAIN_PATH_PREFIX=${PREBUILT_DIR}

execute () {
if ! eval "$1"; then
echo "command:\"$1\" error"
exit 1
fi
}

execute "mkdir -p $WORK_DIR"

# Call the cleanup function when this tool exits.
trap cleanup EXIT

# Download and install the toolchain from
# https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download
download_file() {
local file_name="$1"
local install_path="$2"
local file_sha="$3"

echo "Install $1 to $2"
if [[ "$(ls -A "$2")" ]]; then
read -rp "The file already exists. Keep it (y/n)? " replaced
case ${replaced:0:1} in
y|Y )
echo "Skip download $1."
return
;;
* )
rm -rf "$2"
;;
esac
fi

echo "Download ${file_name} ..."
mkdir -p "$install_path"
wget --progress=bar:force:noscroll --directory-prefix="${WORK_DIR}" \
"${RISCV_GNU_TOOLCHAIN_URL}/${TOOLCHAIN_VERSION}/${file_name}" && \
echo "${file_sha} ${WORK_DIR}/${file_name}" | sha256sum -c -
echo "Extract ${file_name} ..."
tar -C "${install_path}" -xf "${WORK_DIR}/${file_name}" --no-same-owner \
--strip-components=1
}


read -rp "Install RISCV toolchain(y/n)? " answer
case ${answer:0:1} in
y|Y )
download_file "${RISCV_TOOLCHAIN_FILE_NAME}" \
"${TOOLCHAIN_PATH_PREFIX}" \
"${RISCV_TOOLCHAIN_FILE_SHA}"
;;
* )
echo "Skip install RISCV toolchain."
;;
esac
echo "download finished."


# RISC-V cross compilation in GNU/Linux
RISCV_TOOLCHAIN_ROOT=${TOOLCHAIN_PATH_PREFIX}
RISCV_QEMU_PATH=${TOOLCHAIN_PATH_PREFIX}/bin/qemu-riscv64
python3 "${ORT_ROOT_DIR}"/tools/ci_build/build.py \
--build_dir "${ORT_ROOT_DIR}/build/${DIR_OS}" \
--rv64 \
--parallel \
--skip_tests \
--config RelWithDebInfo \
--cmake_generator=Ninja \
--riscv_qemu_path="${RISCV_QEMU_PATH}" \
--riscv_toolchain_root="${RISCV_TOOLCHAIN_ROOT}" "$@"


Loading