Skip to content

Commit 3915c4a

Browse files
committed
Add experimental ability to build LLVM libc.
This is extremely new and barely tested: I've compiled and run a hello-world program, and that is literally all. But that's a start – if you can do that, you can also investigate what else does and doesn't compile or run successfully. So this is enough to allow users to experiment. As documented in the new `docs/llvmlibc.md`, this currently builds C libraries only (not C++), for AArch32 only. I don't know how much effort it will take to fix each of those: I only know that in each case _at least one_ cause of build failure exists, but I don't know how many more are hiding behind that one. You can use the new `-DLLVM_TOOLCHAIN_C_LIBRARY` with or without the option to build an overlay package. If you have both a newlib and an llvm-libc overlay package, then it should be possible to install both alongside each other on top of the same main toolchain. Unlike picolibc and newlib, llvm-libc doesn't come with a crt0.o startup file. Compiled for bare metal (as we're doing here), it also leaves some support functions undefined (e.g. for writing to stdout or stderr), expecting the user to provide them. So I've also added a small source directory containing the missing pieces. That builds two additional libraries, -lcrt0 and -lsemihost. At present, I haven't set up a llvm-libc specific ld script, and there's no code in my new crt0 that copies RW data out of ROM (as there is in picolibc). The only definition required by the crt0 is `__stack`, which you can define via an ld script of your own if you want, or directly on the ld.lld command ilne if you prefer.
1 parent 4579e2c commit 3915c4a

File tree

13 files changed

+650
-41
lines changed

13 files changed

+650
-41
lines changed

CMakeLists.txt

Lines changed: 207 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ set(LLVM_TOOLCHAIN_C_LIBRARY
140140
"Which C library to use."
141141
)
142142
set_property(CACHE LLVM_TOOLCHAIN_C_LIBRARY
143-
PROPERTY STRINGS picolibc newlib)
143+
PROPERTY STRINGS picolibc newlib llvmlibc)
144144

145145
# Previously, the LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL option was
146146
# called LLVM_TOOLCHAIN_NEWLIB_OVERLAY_INSTALL. Detect a setting of
@@ -153,7 +153,7 @@ else()
153153
set(overlay_install_default OFF)
154154
endif()
155155
option(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL
156-
"Make cpack build an overlay package that can be unpacked over the main toolchain to install a secondary set of libraries based on newlib."
156+
"Make cpack build an overlay package that can be unpacked over the main toolchain to install a secondary set of libraries based on newlib or llvm-libc."
157157
${overlay_install_default})
158158
if(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL)
159159
if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL "picolibc")
@@ -268,7 +268,9 @@ function(read_repo_version output_variable_prefix repo)
268268
set(${output_variable_prefix}_SHALLOW "${shallow}" PARENT_SCOPE)
269269
endfunction()
270270
read_repo_version(llvmproject llvm-project)
271-
read_repo_version(${LLVM_TOOLCHAIN_C_LIBRARY} ${LLVM_TOOLCHAIN_C_LIBRARY})
271+
if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo?
272+
read_repo_version(${LLVM_TOOLCHAIN_C_LIBRARY} ${LLVM_TOOLCHAIN_C_LIBRARY})
273+
endif()
272274

273275
# The patches are generated from custom branch, with followin command:
274276
# git format-patch -k origin/main
@@ -319,7 +321,9 @@ FetchContent_Declare(newlib
319321
)
320322

321323
FetchContent_MakeAvailable(llvmproject)
322-
FetchContent_MakeAvailable(${LLVM_TOOLCHAIN_C_LIBRARY})
324+
if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo?
325+
FetchContent_MakeAvailable(${LLVM_TOOLCHAIN_C_LIBRARY})
326+
endif()
323327

324328
# We generally want to install to a local directory to see what the
325329
# output will look like rather than install into the system, so change
@@ -334,6 +338,10 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
334338
)
335339
endif()
336340

341+
# Whether to try to build C++ libraries. (We can't currently do this
342+
# for all choices of C library.)
343+
set(CXX_LIBS ON)
344+
337345
if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL newlib)
338346
install(
339347
FILES
@@ -350,6 +358,42 @@ install(
350358
COMPONENT llvm-toolchain-omax-cfg
351359
)
352360

361+
if(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)
362+
install(
363+
FILES
364+
${CMAKE_CURRENT_SOURCE_DIR}/llvmlibc.cfg
365+
DESTINATION bin
366+
COMPONENT llvm-toolchain-llvmlibc-configs
367+
)
368+
369+
# We aren't yet able to build C++ libraries to go with llvm-libc
370+
set(CXX_LIBS OFF)
371+
372+
# We need to build libc-hdrgen
373+
ExternalProject_Add(
374+
libc_hdrgen
375+
SOURCE_DIR ${llvmproject_SOURCE_DIR}/llvm
376+
DEPENDS ${lib_tool_dependencies}
377+
CMAKE_ARGS
378+
-DLLVM_ENABLE_RUNTIMES=libc
379+
-DLLVM_LIBC_FULL_BUILD=ON
380+
-DCMAKE_BUILD_TYPE=Debug
381+
STEP_TARGETS build install
382+
BUILD_COMMAND ${CMAKE_COMMAND} --build . --target libc-hdrgen
383+
INSTALL_COMMAND ${CMAKE_COMMAND} -E true
384+
# Always run the build command so that incremental builds are correct.
385+
BUILD_ALWAYS TRUE
386+
CONFIGURE_HANDLED_BY_BUILD TRUE
387+
)
388+
ExternalProject_Get_property(libc_hdrgen BINARY_DIR)
389+
set(LIBC_HDRGEN ${BINARY_DIR}/bin/libc-hdrgen${CMAKE_EXECUTABLE_SUFFIX})
390+
391+
# Add an empty check target, to simplify the logic below that expects to
392+
# find one for every libc type. We have no current setup to run the LLVM
393+
# libc test suite.
394+
add_custom_target(check-llvmlibc)
395+
endif()
396+
353397
add_subdirectory(
354398
${llvmproject_SOURCE_DIR}/llvm llvm
355399
)
@@ -396,7 +440,7 @@ set(CPACK_ARCHIVE_COMPONENT_INSTALL TRUE)
396440
# Don't create a separate archive for each component.
397441
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
398442
# When extracting the files put them in an ArmCompiler-.../ directory.
399-
# Exception: the newlib overlay package does not do this, because it has
443+
# Exception: the overlay packages do not do this, because they have
400444
# to be able to unpack over the top of an existing installation on all
401445
# platforms, and each platform has a different top-level directory name.
402446
if(LLVM_TOOLCHAIN_LIBRARY_OVERLAY_INSTALL)
@@ -479,7 +523,7 @@ add_custom_target(
479523
-DLLVMEmbeddedToolchainForArm_VERSION=${LLVMEmbeddedToolchainForArm_VERSION}
480524
-DLLVMEmbeddedToolchainForArm_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
481525
-Dllvmproject_SOURCE_DIR=${llvmproject_SOURCE_DIR}
482-
# only one of picolibc and newlib source dirs is needed, but easiest to
526+
# at most one of picolibc and newlib source dirs is needed, but easiest to
483527
# specify both definitions
484528
-Dpicolibc_SOURCE_DIR=${picolibc_SOURCE_DIR}
485529
-Dnewlib_SOURCE_DIR=${newlib_SOURCE_DIR}
@@ -817,6 +861,112 @@ function(
817861
)
818862
endfunction()
819863

864+
function(
865+
add_llvmlibc
866+
directory
867+
variant
868+
target_triple
869+
flags
870+
test_executor_params
871+
default_boot_flash_addr
872+
default_boot_flash_size
873+
default_flash_addr
874+
default_flash_size
875+
default_ram_addr
876+
default_ram_size
877+
default_stack_size
878+
)
879+
get_runtimes_flags("${directory}" "${flags}")
880+
881+
set(runtimes_flags "${runtimes_flags} -Wno-error=atomic-alignment")
882+
883+
set(common_cmake_args
884+
-DCMAKE_AR=${LLVM_BINARY_DIR}/bin/llvm-ar${CMAKE_EXECUTABLE_SUFFIX}
885+
-DCMAKE_ASM_COMPILER=${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}
886+
-DCMAKE_ASM_COMPILER_TARGET=${target_triple}
887+
-DCMAKE_ASM_FLAGS=${runtimes_flags}
888+
-DCMAKE_BUILD_TYPE=Release
889+
-DCMAKE_CXX_COMPILER=${LLVM_BINARY_DIR}/bin/clang++${CMAKE_EXECUTABLE_SUFFIX}
890+
-DCMAKE_CXX_COMPILER_TARGET=${target_triple}
891+
-DCMAKE_CXX_FLAGS=${runtimes_flags}
892+
-DCMAKE_C_COMPILER=${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}
893+
-DCMAKE_C_COMPILER_TARGET=${target_triple}
894+
-DCMAKE_C_FLAGS=${runtimes_flags}
895+
-DCMAKE_INSTALL_MESSAGE=${CMAKE_INSTALL_MESSAGE}
896+
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
897+
-DCMAKE_NM=${LLVM_BINARY_DIR}/bin/llvm-nm${CMAKE_EXECUTABLE_SUFFIX}
898+
-DCMAKE_RANLIB=${LLVM_BINARY_DIR}/bin/llvm-ranlib${CMAKE_EXECUTABLE_SUFFIX}
899+
# Let CMake know we're cross-compiling
900+
-DCMAKE_SYSTEM_NAME=Generic
901+
-DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY
902+
)
903+
904+
ExternalProject_Add(
905+
llvmlibc_${variant}
906+
SOURCE_DIR ${llvmproject_SOURCE_DIR}/runtimes
907+
PREFIX llvmlibc/${variant}
908+
INSTALL_DIR llvmlibc/${variant}/install
909+
DEPENDS ${lib_tool_dependencies} ${libc_target} libc_hdrgen
910+
CMAKE_ARGS
911+
${common_cmake_args}
912+
-DLIBC_TARGET_TRIPLE=${target_triple}
913+
-DLIBC_HDRGEN_EXE=${LIBC_HDRGEN}
914+
-DLIBC_TARGET_OS=baremetal
915+
-DLLVM_CMAKE_DIR=${LLVM_BINARY_DIR}/lib/cmake/llvm
916+
-DLLVM_ENABLE_PER_TARGET_RUNTIME_DIR=ON
917+
-DLLVM_ENABLE_RUNTIMES=libc
918+
-DLLVM_INCLUDE_TESTS=OFF # I haven't yet got the tests to build
919+
-DLLVM_LIBC_FULL_BUILD=ON
920+
STEP_TARGETS build install
921+
USES_TERMINAL_CONFIGURE FALSE
922+
USES_TERMINAL_BUILD TRUE
923+
USES_TERMINAL_INSTALL TRUE
924+
USES_TERMINAL_TEST TRUE
925+
LIST_SEPARATOR ,
926+
# Always run the build command so that incremental builds are correct.
927+
BUILD_ALWAYS TRUE
928+
CONFIGURE_HANDLED_BY_BUILD TRUE
929+
INSTALL_COMMAND ${CMAKE_COMMAND} --install .
930+
# Copy llvm-libc lib directory, moving libraries out of their
931+
# target-specific subdirectory.
932+
COMMAND
933+
${CMAKE_COMMAND}
934+
-E copy_directory
935+
<INSTALL_DIR>/lib/${target_triple}
936+
"${LLVM_BINARY_DIR}/${directory}/lib"
937+
# And copy the include directory, which is already arranged right.
938+
COMMAND
939+
${CMAKE_COMMAND}
940+
-E copy_directory
941+
<INSTALL_DIR>/include
942+
"${LLVM_BINARY_DIR}/${directory}/include"
943+
)
944+
945+
ExternalProject_Add(
946+
llvmlibc-support_${variant}
947+
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/llvmlibc-support
948+
PREFIX llvmlibc-support/${variant}
949+
INSTALL_DIR "${LLVM_BINARY_DIR}/${directory}"
950+
DEPENDS ${lib_tool_dependencies} llvmlibc_${variant}-install
951+
CMAKE_ARGS ${common_cmake_args}
952+
STEP_TARGETS build install
953+
USES_TERMINAL_CONFIGURE FALSE
954+
USES_TERMINAL_BUILD TRUE
955+
USES_TERMINAL_INSTALL TRUE
956+
USES_TERMINAL_TEST TRUE
957+
LIST_SEPARATOR ,
958+
# Always run the build command so that incremental builds are correct.
959+
BUILD_ALWAYS TRUE
960+
CONFIGURE_HANDLED_BY_BUILD TRUE
961+
)
962+
963+
add_dependencies(
964+
llvm-toolchain-runtimes
965+
llvmlibc_${variant}
966+
llvmlibc-support_${variant}
967+
)
968+
endfunction()
969+
820970
macro(
821971
add_libc
822972
directory
@@ -869,6 +1019,21 @@ macro(
8691019
"${default_ram_size}"
8701020
"${default_stack_size}"
8711021
)
1022+
elseif(LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)
1023+
add_llvmlibc(
1024+
"${directory}"
1025+
"${variant}"
1026+
"${target_triple}"
1027+
"${flags}"
1028+
"${test_executor_params}"
1029+
"${default_boot_flash_addr}"
1030+
"${default_boot_flash_size}"
1031+
"${default_flash_addr}"
1032+
"${default_flash_size}"
1033+
"${default_ram_addr}"
1034+
"${default_ram_size}"
1035+
"${default_stack_size}"
1036+
)
8721037
endif()
8731038
endmacro()
8741039

@@ -1231,25 +1396,29 @@ function(add_library_variant target_arch)
12311396
"${VARIANT_COMPILE_FLAGS}"
12321397
"${lit_test_executor}"
12331398
"${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install"
1234-
)
1235-
add_libcxx_libcxxabi_libunwind(
1236-
"${directory}"
1237-
"${variant}"
1238-
"${target_triple}"
1239-
"${VARIANT_COMPILE_FLAGS}"
1240-
"${lit_test_executor}"
1241-
"${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install"
1242-
"${${LLVM_TOOLCHAIN_C_LIBRARY}_specific_runtimes_options}"
1243-
${VARIANT_ENABLE_EXCEPTIONS}
1244-
${VARIANT_ENABLE_RTTI}
1245-
)
1399+
)
1400+
if(CXX_LIBS)
1401+
add_libcxx_libcxxabi_libunwind(
1402+
"${directory}"
1403+
"${variant}"
1404+
"${target_triple}"
1405+
"${VARIANT_COMPILE_FLAGS}"
1406+
"${lit_test_executor}"
1407+
"${LLVM_TOOLCHAIN_C_LIBRARY}_${variant}-install"
1408+
"${${LLVM_TOOLCHAIN_C_LIBRARY}_specific_runtimes_options}"
1409+
${VARIANT_ENABLE_EXCEPTIONS}
1410+
${VARIANT_ENABLE_RTTI}
1411+
)
1412+
endif()
12461413
if(VARIANT_COMPILE_FLAGS MATCHES "-march=armv8")
12471414
message("C++ runtime libraries tests disabled for ${variant}")
12481415
else()
12491416
add_custom_target(check-llvm-toolchain-runtimes-${variant})
12501417
add_dependencies(check-llvm-toolchain-runtimes check-llvm-toolchain-runtimes-${variant})
12511418
add_compiler_rt_tests("${variant}")
1252-
add_libcxx_libcxxabi_libunwind_tests("${variant}")
1419+
if(CXX_LIBS)
1420+
add_libcxx_libcxxabi_libunwind_tests("${variant}")
1421+
endif()
12531422
endif()
12541423
endif()
12551424

@@ -1333,21 +1502,25 @@ set(multilib_yaml_content "")
13331502
# For most variants, the "flash" memory is placed in address range, where
13341503
# simulated boards have RAM. This is because code for some tests does not fit
13351504
# the real flash.
1336-
add_library_variants_for_cpu(
1337-
aarch64
1338-
COMPILE_FLAGS "-march=armv8-a"
1339-
MULTILIB_FLAGS "--target=aarch64-unknown-none-elf"
1340-
PICOLIBC_BUILD_TYPE "release"
1341-
QEMU_MACHINE "virt"
1342-
QEMU_CPU "cortex-a57"
1343-
BOOT_FLASH_ADDRESS 0x40000000
1344-
BOOT_FLASH_SIZE 0x1000
1345-
FLASH_ADDRESS 0x40001000
1346-
FLASH_SIZE 0xfff000
1347-
RAM_ADDRESS 0x41000000
1348-
RAM_SIZE 0x1000000
1349-
STACK_SIZE 8K
1350-
)
1505+
if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc))
1506+
# llvm-libc doesn't have a bare-metal configuration for AArch64, so we
1507+
# leave out the AArch64 library if we're building that libc.
1508+
add_library_variants_for_cpu(
1509+
aarch64
1510+
COMPILE_FLAGS "-march=armv8-a"
1511+
MULTILIB_FLAGS "--target=aarch64-unknown-none-elf"
1512+
PICOLIBC_BUILD_TYPE "release"
1513+
QEMU_MACHINE "virt"
1514+
QEMU_CPU "cortex-a57"
1515+
BOOT_FLASH_ADDRESS 0x40000000
1516+
BOOT_FLASH_SIZE 0x1000
1517+
FLASH_ADDRESS 0x40001000
1518+
FLASH_SIZE 0xfff000
1519+
RAM_ADDRESS 0x41000000
1520+
RAM_SIZE 0x1000000
1521+
STACK_SIZE 8K
1522+
)
1523+
endif()
13511524
# For AArch32, clang uses different defaults for FPU selection than GCC, both
13521525
# when "+fp" or "+fp.dp" are used and when no FPU specifier is provided in
13531526
# "-march=". Using "-mfpu=" explicitly.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ bare-metal LLVM based toolchain targeting Arm based on:
77
* libc++abi
88
* libc++
99
* compiler-rt
10-
* picolibc, or optionally newlib
10+
* picolibc, or optionally newlib or LLVM's libc
1111

1212
## Goal
1313

cmake/generate_version_txt.cmake

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ execute_process(
2323
OUTPUT_STRIP_TRAILING_WHITESPACE
2424
COMMAND_ERROR_IS_FATAL ANY
2525
)
26-
execute_process(
27-
COMMAND git -C ${${LLVM_TOOLCHAIN_C_LIBRARY}_SOURCE_DIR} rev-parse HEAD
28-
OUTPUT_VARIABLE ${LLVM_TOOLCHAIN_C_LIBRARY}_COMMIT
29-
OUTPUT_STRIP_TRAILING_WHITESPACE
30-
COMMAND_ERROR_IS_FATAL ANY
31-
)
26+
if(NOT (LLVM_TOOLCHAIN_C_LIBRARY STREQUAL llvmlibc)) # libc in a separate repo?
27+
execute_process(
28+
COMMAND git -C ${${LLVM_TOOLCHAIN_C_LIBRARY}_SOURCE_DIR} rev-parse HEAD
29+
OUTPUT_VARIABLE ${LLVM_TOOLCHAIN_C_LIBRARY}_COMMIT
30+
OUTPUT_STRIP_TRAILING_WHITESPACE
31+
COMMAND_ERROR_IS_FATAL ANY
32+
)
33+
endif()
3234

3335
configure_file(
3436
${CMAKE_CURRENT_LIST_DIR}/VERSION.txt.in

0 commit comments

Comments
 (0)