Build LLVM (Cross) Compiler for Risc‐V - soccercheng/pocl-riscv GitHub Wiki

LLVM Directory Structure

#
# Use the following command to organize LLVM directory structure
#
$ export LLVM_VERSION=20.x
$ mkdir llvm; cd llvm
$ mkdir build_${LLVM_VERSION}_cross.riscv_RelWithDebInfo
$ mkdir build_${LLVM_VERSION}_riscv_RelWithDebInfo
$ gitl clone https://github.com/llvm/llvm-project.git
$ cd llvm-project
$ git checkout origin/release/${LLVM_VERSION}

$ export LLVM_SRC=$(realpath -q .)

The directory structure of LLVM looks like the following

./llvm
    ├── build_20.x_cross.riscv_RelWithDebInfo
    ├── build_20.x_riscv_RelWithDebInfo
    └── llvm-project

Build LLVM Cross Compiler for Risc-V

$ cd build_${LLVM_VERSION}_cross.riscv_RelWithDebInfo

#
# USE_SPLIT_DWARF==True or BUILD_TYPE==Debug might cause
# build fails due to size problem on risc-v platform 
#
export LLVM_VERSION=20.x
export USE_SPLIT_DWARF=False
#export BUILD_TYPE=[Release|Debug|RelWithDebInfo|MinSizeRel]
export BUILD_TYPE=RelWithDebInfo
export ENABLE_PROJECTS="clang;lld;clang-tools-extra;polly;mlir"
#export ENABLE_RUNTIMES="libc;libcxxabi;pstl;libcxx"
unset ENABLE_RUNTIMES
export INSTALL_PREFIX=/usr/local/llvm_${LLVM_VERSION}_cross.riscv_${BUILD_TYPE}

$ cmake -S ${LLVM_SRC}/llvm -B . -G "Ninja"        \
    -DCMAKE_C_COMPILER=clang                  \
    -DCMAKE_CXX_COMPILER=clang++              \
    -DCLANG_DEFAULT_LINKER=lld                \
    -DLLVM_ENABLE_PROJECTS=${ENABLE_PROJECTS} \
    -DLLVM_ENABLE_RUNTIMES=${ENABLE_RUNTIMES} \
    -DCMAKE_BUILD_TYPE=${BUILD_TYPE}          \
    -DLLVM_BUILD_LLVM_DYLIB=True              \
    -DLLVM_USE_SPLIT_DWARF=${USE_SPLIT_DWARF} \
    -DLLVM_OPTIMIZED_TABLEGEN=True -DLLVM_CCACHE_BUILD=OFF              \
    -DLLVM_INSTALL_UTILS=ON -DLLVM_ENABLE_RTTI=TRUE -DLLVM_ENABLE_EH=ON \
    -DLLVM_ENABLE_Z3_SOLVER=OFF -DHAVE_POSIX_REGEX=0                    \
    -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_TESTS=OFF                 \
    \
    -DCMAKE_SYSTEM_NAME=Linux                                \
    -DLLVM_TARGETS_TO_BUILD="RISCV"                          \
    -DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-linux-gnu   \
    \
    -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}

# Build 
#     For unknown reason, in debug build, USE_SPLIT_DWARF could NOT be turned on
#     please use ninja -j <smaller-number> in case RAM/SWAP shortage
# sudo mkdir ${INSTALL_PREFIX}
$ ninja -j $(nproc)
$ ninja install

Build LLVM Native Compiler for Risc-V

$ cd build_${LLVM_VERSION}_riscv_RelWithDebInfo

#
# USE_SPLIT_DWARF==True or BUILD_TYPE==Debug might cause
# build fails due to size problem on risc-v platform 
#
export USE_SPLIT_DWARF=False
#export BUILD_TYPE=[Release|Debug|RelWithDebInfo|MinSizeRel]
export BUILD_TYPE=RelWithDebInfo
export ENABLE_PROJECTS="clang;lld;clang-tools-extra;lldb;polly;mlir"
#export ENABLE_RUNTIMES="libc;libcxxabi;pstl;libcxx"
unset ENABLE_RUNTIMES
export INSTALL_PREFIX=/usr/local/llvm_${LLVM_VERSION}_riscv_${BUILD_TYPE}

$ cmake -S ${LLVM_SRC}/llvm -B . -G "Ninja"           \
    -DCMAKE_TOOLCHAIN_FILE=./riscv64.toolchain.cmake  \
    \
    -DCLANG_DEFAULT_LINKER=lld                \
    -DLLVM_ENABLE_PROJECTS=${ENABLE_PROJECTS} \
    -DLLVM_ENABLE_RUNTIMES=${ENABLE_RUNTIMES} \
    -DCMAKE_BUILD_TYPE=${BUILD_TYPE}          \
    -DLLVM_BUILD_LLVM_DYLIB=True              \
    -DLLVM_USE_SPLIT_DWARF=${USE_SPLIT_DWARF} \
    -DLLVM_OPTIMIZED_TABLEGEN=True -DLLVM_CCACHE_BUILD=OFF              \
    -DLLVM_INSTALL_UTILS=ON -DLLVM_ENABLE_RTTI=TRUE -DLLVM_ENABLE_EH=ON \
    -DLLVM_ENABLE_Z3_SOLVER=OFF -DHAVE_POSIX_REGEX=0                    \
    -DLLVM_ENABLE_BINDINGS=OFF -DLLVM_INCLUDE_TESTS=OFF                 \
    \
    -DCMAKE_SYSTEM_NAME=Linux                                \
    -DLLVM_TARGETS_TO_BUILD="RISCV"                          \
    -DLLVM_DEFAULT_TARGET_TRIPLE=riscv64-unknown-linux-gnu   \
    \
    -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX}

# Build 
#     For unknown reason, in debug build, USE_SPLIT_DWARF could NOT be turned on
#     please use ninja -j <smaller-number> in case RAM/SWAP shortage
# sudo mkdir ${INSTALL_PREFIX}
$ ninja -j $(nproc)
$ ninja install

##
# Very Important ........
#
$ sudo mv /usr/local/llvm_${LLVM_VERSION}_riscv_${BUILD_TYPE}/bin/llvm-config \
      /usr/local/llvm_${LLVM_VERSION}_riscv_${BUILD_TYPE}/bin/llvm-config.orig
$ sudo cp -a /usr/local/llvm_${LLVM_VERSION}_cross.riscv_${BUILD_TYPE}/bin/llvm-config \
      /usr/local/llvm_${LLVM_VERSION}_riscv_${BUILD_TYPE}/bin/llvm-config

riscv64.toolchain.cmake

#include(CMakeForceCompiler)

# usage
# cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/rv32imac.cmake ../

# Look for GCC in path
FIND_FILE( RISCV64_GCC_COMPILER "riscv64-linux-gnu-gcc-14" PATHS ENV INCLUDE)

# Select which is found
if (NOT EXISTS ${RISCV64_GCC_COMPILER})
    message(FATAL_ERROR "RISCV64 GCC not found. ${RISCV64_GCC_COMPILER}")
endif()

message( "RISCV64 GCC found: ${RISCV64_GCC_COMPILER}")

get_filename_component(RISCV64_TOOLCHAIN_BIN_PATH ${RISCV64_GCC_COMPILER} DIRECTORY)
get_filename_component(RISCV64_TOOLCHAIN_BIN_GCC ${RISCV64_GCC_COMPILER} NAME_WE)
get_filename_component(RISCV64_TOOLCHAIN_BIN_EXT ${RISCV64_GCC_COMPILER} EXT)

message( "RISCV64 GCC Path: ${RISCV64_TOOLCHAIN_BIN_PATH}" )

STRING(REGEX REPLACE "\-gcc-14" "-" CROSS_COMPILE ${RISCV64_TOOLCHAIN_BIN_GCC})
message( "RISCV64 Cross Compile: ${CROSS_COMPILE}" )

# The Generic system name is used for embedded targets (targets without OS) in CMake
#set( CMAKE_SYSTEM_NAME          Generic )
set( CMAKE_SYSTEM_NAME          Linux )
set( CMAKE_SYSTEM_PROCESSOR     riscv64 )
#set( CMAKE_EXECUTABLE_SUFFIX    ".elf")

set ( CMAKE_FIND_ROOT_PATH /usr/riscv64-linux-gnu )
set ( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
set ( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY )
set ( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY )

# specify the cross compiler. We force the compiler so that CMake doesn't
# attempt to build a simple test program as this will fail without us using
# the -nostartfiles option on the command line
#CMAKE_FORCE_C_COMPILER( "${AARCH64_TOOLCHAIN_BIN_PATH}/${CROSS_COMPILE}gcc${AARCH64_TOOLCHAIN_BIN_EXT}" GNU )
#CMAKE_FORCE_CXX_COMPILER( "${AARCH64_TOOLCHAIN_BIN_PATH}/${CROSS_COMPILE}g++${AARCH64_TOOLCHAIN_BIN_EXT}" GNU )

# use gcc
set(CMAKE_ASM_COMPILER ${CROSS_COMPILE}gcc-14)
set(CMAKE_AR ${CROSS_COMPILE}ar)
set(CMAKE_LD ${CROSS_COMPILE}ld)
set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc-14)
set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++-14)

# for h/w float
SET(CMAKE_C_FLAGS "-march=rv64imafdczicsr_zifencei -mabi=lp64d")
SET(CMAKE_CXX_FLAGS "-march=rv64imafdczicsr_zifencei -mabi=lp64d")

# We must set the OBJCOPY setting into cache so that it's available to the
# whole project. Otherwise, this does not get set into the CACHE and therefore
# the build doesn't know what the OBJCOPY filepath is
set( CMAKE_OBJCOPY      ${AARCH64_TOOLCHAIN_BIN_PATH}/${CROSS_COMPILE}objcopy
     CACHE FILEPATH "The toolchain objcopy command " FORCE )

set( CMAKE_OBJDUMP      ${AARCH64_TOOLCHAIN_BIN_PATH}/${CROSS_COMPILE}objdump
     CACHE FILEPATH "The toolchain objdump command " FORCE )

# Set the common build flags

# Set the CMAKE C flags (which should also be used by the assembler!
#set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -mcmodel=medium" )

#set( CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "" )
#set( CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "" )
#set( CMAKE_SHARED_LINKER_FLAGS "-latomic" )