From 4f9c10eb48a3725ecfd844d44ccf3068fbadbb85 Mon Sep 17 00:00:00 2001 From: Siva Chandra Reddy Date: Sat, 17 Dec 2022 00:04:23 +0000 Subject: [PATCH] [libc] Add support for standalone cross compilation of libc. One should be able to do a cross build of the libc now. For example, using clang on a x86_64 linux host, one can build for an aarch64 linux target by specifying -DLIBC_TARGET_TRIPLE=aarch64-linux-gnu. Follow up changes will add a baremetal config and also appropriate documentation about cross compiling the libc for CPU targets. Reviewed By: jhuber6 Differential Revision: https://reviews.llvm.org/D140351 --- libc/CMakeLists.txt | 7 +- libc/cmake/modules/LLVMLibCArchitectures.cmake | 150 +++++++++++++++++++++--- libc/cmake/modules/prepare_libc_gpu_build.cmake | 10 +- libc/docs/gpu_mode.rst | 2 +- 4 files changed, 144 insertions(+), 25 deletions(-) diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt index fa0ce82..8eb4583 100644 --- a/libc/CMakeLists.txt +++ b/libc/CMakeLists.txt @@ -55,17 +55,18 @@ option(LLVM_LIBC_FULL_BUILD "Build and test LLVM libc as if it is the full libc" option(LLVM_LIBC_IMPLEMENTATION_DEFINED_TEST_BEHAVIOR "Build LLVM libc tests assuming our implementation-defined behavior" ON) option(LLVM_LIBC_ENABLE_LINTING "Enables linting of libc source files" OFF) -set(LLVM_LIBC_TARGET_OS ${CMAKE_SYSTEM_NAME} CACHE STRING "Target operating system for LLVM libc") -string(TOLOWER ${LLVM_LIBC_TARGET_OS} LIBC_TARGET_OS) +option(LIBC_GPU_BUILD "Build libc for the GPU. All CPU build options will be ignored." OFF) +set(LIBC_TARGET_TRIPLE "" CACHE STRING "The target triple for the libc build.") # Defines LIBC_TARGET_ARCHITECTURE and associated macros. include(LLVMLibCArchitectures) -include(LLVMLibCCheckMPFR) if(LIBC_TARGET_ARCHITECTURE_IS_GPU) include(prepare_libc_gpu_build) endif() +include(LLVMLibCCheckMPFR) + if(LLVM_LIBC_CLANG_TIDY) set(LLVM_LIBC_ENABLE_LINTING ON) endif() diff --git a/libc/cmake/modules/LLVMLibCArchitectures.cmake b/libc/cmake/modules/LLVMLibCArchitectures.cmake index 9da74c4..3c99f64 100644 --- a/libc/cmake/modules/LLVMLibCArchitectures.cmake +++ b/libc/cmake/modules/LLVMLibCArchitectures.cmake @@ -1,25 +1,139 @@ # ------------------------------------------------------------------------------ -# Architecture definitions +# Architecture and OS definitions. +# +# The correct target OS and architecture to build the libc for is deduced here. +# When possible, we also setup appropriate compile options for the target +# platform. # ------------------------------------------------------------------------------ -if(LIBC_TARGET_OS MATCHES "gpu") +if(LIBC_GPU_BUILD) + # We set the generic target and OS to "gpu" here. More specific defintions + # for the exact target GPU are set up in prepare_libc_gpu_build.cmake. + set(LIBC_TARGET_OS "gpu") set(LIBC_TARGET_ARCHITECTURE_IS_GPU TRUE) set(LIBC_TARGET_ARCHITECTURE "gpu") -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips") - set(LIBC_TARGET_ARCHITECTURE_IS_MIPS TRUE) - set(LIBC_TARGET_ARCHITECTURE "mips") -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm") - set(LIBC_TARGET_ARCHITECTURE_IS_ARM TRUE) - set(LIBC_TARGET_ARCHITECTURE "arm") -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64") - set(LIBC_TARGET_ARCHITECTURE_IS_AARCH64 TRUE) - set(LIBC_TARGET_ARCHITECTURE "aarch64") -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(AMD64|amd64)|(^i.86$)") - set(LIBC_TARGET_ARCHITECTURE_IS_X86 TRUE) - set(LIBC_TARGET_ARCHITECTURE "x86_64") -elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)") - set(LIBC_TARGET_ARCHITECTURE_IS_POWER TRUE) - set(LIBC_TARGET_ARCHITECTURE "power") + if(LIBC_TARGET_TRIPLE) + message(WARNING "LIBC_TARGET_TRIPLE is ignored as LIBC_GPU_BUILD is on. ") + endif() + return() +endif() + +if(MSVC) + # If the compiler is visual c++ or equivalent, we will assume a host build. + set(LIBC_TARGET_OS ${CMAKE_HOST_SYSTEM_NAME}) + string(TOLOWER ${LIBC_TARGET_OS} LIBC_TARGET_OS) + set(LIBC_TARGET_ARCHITECTURE ${CMAKE_HOST_SYSTEM_PROCESSOR}) + if(LIBC_TARGET_TRIPLE) + message(WARNING "libc build: Detected MSVC or equivalent compiler; " + "LIBC_TARGET_TRIPLE is ignored and a host build is assumed.") + endif() + return() +endif() + +# A helper function to get the architecture and system components from a target +# triple. +function(get_arch_and_system_from_triple triple arch_var sys_var) + string(REPLACE "-" ";" triple_comps ${triple}) + list(LENGTH triple_comps triple_size) + if(triple_size LESS "3") + return() + endif() + math(EXPR system_index "${triple_size} - 2") + list(GET triple_comps 0 target_arch) + # The target_arch string can have sub-architecture suffixes which we want to + # remove. So, we regex-match the string and set target_arch to a cleaner + # value. + if(target_arch MATCHES "^mips") + set(target_arch "mips") + elseif(target_arch MATCHES "^arm") + set(target_arch "arm") + elseif(target_arch MATCHES "^aarch64") + set(target_arch "aarch64") + elseif(target_arch MATCHES "(x86_64)|(AMD64|amd64)|(^i.86$)") + set(target_arch "x86_64") + elseif(target_arch MATCHES "^(powerpc|ppc)") + set(target_arch "power") + else() + return() + endif() + + set(${arch_var} ${target_arch} PARENT_SCOPE) + list(GET triple_comps ${system_index} target_sys) + set(${sys_var} ${target_sys} PARENT_SCOPE) +endfunction(get_arch_and_system_from_triple) + +# Query the default target triple of the compiler. +set(target_triple_option "-print-target-triple") +if(CMAKE_COMPILER_IS_GNUCXX) + # GCC does not support the "-print-target-triple" option but supports + # "-print-multiarch" which clang does not support for all targets. + set(target_triple_option "-print-multiarch") +endif() +execute_process(COMMAND ${CMAKE_CXX_COMPILER} ${target_triple_option} + RESULT_VARIABLE libc_compiler_triple_check + OUTPUT_VARIABLE libc_compiler_triple) +if(NOT (libc_compiler_triple_check EQUAL "0")) + message(FATAL_ERROR "libc build: error querying target triple from the " + "compiler: ${libc_compiler_triple}") +endif() +get_arch_and_system_from_triple(${libc_compiler_triple} + compiler_arch compiler_sys) +if(NOT compiler_arch) + message(FATAL_ERROR + "libc build: Invalid or unknown libc compiler target triple: " + "${libc_compiler_triple}") +endif() + +set(LIBC_TARGET_ARCHITECTURE ${compiler_arch}) +set(LIBC_TARGET_OS ${compiler_sys}) + +# The libc's target architecture and OS are set to match the compiler's default +# target triple above. However, one can explicitly set LIBC_TARGET_TRIPLE. If it +# is and does not match the compiler's target triple, then we will use it set up +# libc's target architecture and OS. +if(LIBC_TARGET_TRIPLE) + get_arch_and_system_from_triple(${LIBC_TARGET_TRIPLE} libc_arch libc_sys) + if(NOT libc_arch) + message(FATAL_ERROR + "libc build: Invalid or unknown triple in LIBC_TARGET_TRIPLE: " + "${LIBC_TARGET_TRIPLE}") + endif() + set(LIBC_TARGET_ARCHITECTURE ${libc_arch}) + set(LIBC_TARGET_OS ${libc_sys}) +endif() + +if((LIBC_TARGET_OS STREQUAL "unknown") OR (LIBC_TARGET_OS STREQUAL "none")) + # We treat "unknown" and "none" systems as baremetal targets. + set(LIBC_TARGET_OS "baremetal") +endif() + +# Set up some convenient vars to make conditionals easy to use in other parts of +# the libc CMake infrastructure. Also, this is where we also check if the target +# architecture is currently supported. +if(LIBC_TARGET_ARCHITECTURE STREQUAL "arm") + set(LIBC_LIBC_TARGET_ARCHITECTUREITECTURE_IS_ARM TRUE) +elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "aarch64") + set(LIBC_LIBC_TARGET_ARCHITECTUREITECTURE_IS_AARCH64 TRUE) +elseif(LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64") + set(LIBC_LIBC_TARGET_ARCHITECTUREITECTURE_IS_X86 TRUE) else() - message(FATAL_ERROR "Unsupported processor ${CMAKE_SYSTEM_PROCESSOR}") + message(FATAL_ERROR + "Unsupported libc target architecture ${LIBC_TARGET_ARCHITECTURE}") +endif() + +# If the compiler target triple is not the same as the triple specified by +# LIBC_TARGET_TRIPLE, we will add a --target option if the compiler is clang. +# If the compiler is GCC we just error out as there is no equivalent of an +# option like --target. +if(LIBC_TARGET_TRIPLE AND + (NOT (libc_compiler_triple STREQUAL LIBC_TARGET_TRIPLE))) + if(CMAKE_COMPILER_IS_GNUCXX) + message(FATAL_ERROR + "GCC target triple and LIBC_TARGET_TRIPLE do not match.") + else() + list(APPEND LIBC_COMPILE_OPTIONS_DEFAULT "--target=${LIBC_TARGET_TRIPLE}") + endif() endif() + +message(STATUS + "Building libc for ${LIBC_TARGET_ARCHITECTURE} on ${LIBC_TARGET_OS}") diff --git a/libc/cmake/modules/prepare_libc_gpu_build.cmake b/libc/cmake/modules/prepare_libc_gpu_build.cmake index 6209094..20fa57f 100644 --- a/libc/cmake/modules/prepare_libc_gpu_build.cmake +++ b/libc/cmake/modules/prepare_libc_gpu_build.cmake @@ -1,3 +1,8 @@ +if(NOT LIBC_TARGET_ARCHITECTURE_IS_GPU) + message(FATAL_ERROR + "libc build: Invalid attempt to set up GPU architectures.") +endif() + # Set up the target architectures to build the GPU libc for. set(all_gpu_architectures "sm_35;sm_37;sm_50;sm_52;sm_53;sm_60;sm_61;sm_62;" "sm_70;sm_72;sm_75;sm_80;sm_86;gfx700;gfx701;gfx801;" @@ -13,9 +18,8 @@ endif() # Ensure the compiler is a valid clang when building the GPU target. set(req_ver "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") -if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND - NOT (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" AND - ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL "${req_ver}")) +if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" AND + ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL "${req_ver}")) message(FATAL_ERROR "Cannot build libc for GPU. CMake compiler " "'${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}' " " is not `Clang ${req_ver}.") diff --git a/libc/docs/gpu_mode.rst b/libc/docs/gpu_mode.rst index b95dba3..b71b6ee 100644 --- a/libc/docs/gpu_mode.rst +++ b/libc/docs/gpu_mode.rst @@ -44,7 +44,7 @@ configuration will look like this: -DLLVM_ENABLE_RUNTIMES="libc;openmp" \ -DCMAKE_BUILD_TYPE= \ # Select build type -DLLVM_LIBC_FULL_BUILD=ON \ # We need the full libc - -DLLVM_LIBC_TARGET_OS=gpu \ # Build in GPU mode + -DLIBC_GPU_BUILD=ON \ # Build in GPU mode -DLLVM_LIBC_GPU_ARCHITECTURES=all \ # Build all supported architectures -DCMAKE_INSTALL_PREFIX= \ # Where 'libcgpu.a' will live $> ninja install -- 2.7.4