Skip to content

Commit

Permalink
[libc] Add initial support for a libc implementation for the GPU
Browse files Browse the repository at this point in the history
This patch contains the initial support for building LLVM's libc as a
target for the GPU. Currently this only supports a handful of very basic
functions that can be implemented without an operating system. The GPU
code is build using the existing OpenMP toolchain. This allows us to
minimally change the existing codebase and get a functioning static
library. This patch allows users to create a static library called
`libcgpu.a` that contains fat binaries containing device IR.

Current limitations are the lack of test support and the fact that only
one target OS can be built at a time. That is, the user cannot get a
`libc` for Linux and one for the GPU simultaneously.

This introduces two new CMake variables to control the behavior
`LLVM_LIBC_TARET_OS` is exported so the user can now specify it to equal
`"gpu"`. `LLVM_LIBC_GPU_ARCHITECTURES` is also used to configure how
many targets to build for at once.

Depends on D138607

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D138608
  • Loading branch information
jhuber6 committed Nov 29, 2022
1 parent d85699e commit 55151e1
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 13 deletions.
41 changes: 32 additions & 9 deletions libc/CMakeLists.txt
Expand Up @@ -23,13 +23,6 @@ endif()
# Path libc/scripts directory.
set(LIBC_BUILD_SCRIPTS_DIR "${LIBC_SOURCE_DIR}/utils/build_scripts")

set(LIBC_TARGET_OS ${CMAKE_SYSTEM_NAME})
string(TOLOWER ${LIBC_TARGET_OS} LIBC_TARGET_OS)

# Defines LIBC_TARGET_ARCHITECTURE and associated macros.
include(LLVMLibCArchitectures)
include(LLVMLibCCheckMPFR)

# Flags to pass down to the compiler while building the libc functions.
set(LIBC_COMPILE_OPTIONS_DEFAULT "" CACHE STRING "Architecture to tell clang to optimize for (e.g. -march=... or -mcpu=...)")

Expand Down Expand Up @@ -58,6 +51,31 @@ 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 up the target architectures to build for the GPU
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;gfx803;gfx900;gfx902;gfx906;gfx908;gfx90a;gfx90c;gfx940;gfx1010;gfx1030;gfx1031;gfx1032;gfx1033;gfx1034;gfx1035;gfx1036;gfx1100;gfx1101;gfx1102;gfx1103")
set(LLVM_LIBC_GPU_ARCHITECTURES ${ALL_GPU_ARCHITECTURES} CACHE STRING "List of GPU architectures to support in LLVM libc")
if (LLVM_LIBC_GPU_ARCHITECTURES STREQUAL "all")
set(LIBC_GPU_ARCHITECTURES ${ALL_GPU_ARCHITECTURES})
else()
set(LIBC_GPU_ARCHITECTURES ${LLVM_LIBC_GPU_ARCHITECTURES})
endif()

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)

# Defines LIBC_TARGET_ARCHITECTURE and associated macros.
include(LLVMLibCArchitectures)
include(LLVMLibCCheckMPFR)

# Ensure the compiler is a valid clang when building the GPU target.
if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT (CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang" AND
${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}"))
message(FATAL_ERROR "Cannot build GPU library, CMake compiler '${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}' is not `Clang ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}`")
endif()
if(LIBC_TARGET_ARCHITECTURE_IS_GPU AND NOT LLVM_LIBC_FULL_BUILD)
message(FATAL_ERROR "Cannot build GPU library, LLVM_LIBC_FULL_BUILD must be enabled")
endif()

if(LLVM_LIBC_CLANG_TIDY)
set(LLVM_LIBC_ENABLE_LINTING ON)
endif()
Expand Down Expand Up @@ -156,7 +174,11 @@ if(LLVM_LIBC_FULL_BUILD)
set(LIBC_COMPONENT libc)
set(LIBC_INSTALL_DEPENDS "libc;install-libc-headers;libc-startup")
set(LIBC_INSTALL_TARGET install-libc)
set(LIBC_ARCHIVE_NAME c)
if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
set(LIBC_ARCHIVE_NAME cgpu)
else()
set(LIBC_ARCHIVE_NAME c)
endif()
else()
set(LIBC_TARGET llvmlibc)
set(LIBC_COMPONENT llvmlibc)
Expand All @@ -179,8 +201,9 @@ endif()
# The lib and test directories are added at the very end as tests
# and libraries potentially draw from the components present in all
# of the other directories.
# TODO: Add testing support for the libc GPU target.
add_subdirectory(lib)
if(LLVM_INCLUDE_TESTS)
if(LLVM_INCLUDE_TESTS AND NOT LIBC_TARGET_ARCHITECTURE_IS_GPU)
add_subdirectory(test)
add_subdirectory(fuzzing)
endif()
Expand Down
5 changes: 4 additions & 1 deletion libc/cmake/modules/LLVMLibCArchitectures.cmake
Expand Up @@ -2,7 +2,10 @@
# Architecture definitions
# ------------------------------------------------------------------------------

if(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
if(LIBC_TARGET_OS MATCHES "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")
Expand Down
11 changes: 11 additions & 0 deletions libc/cmake/modules/LLVMLibCObjectRules.cmake
Expand Up @@ -50,6 +50,17 @@ function(_get_common_compile_options output_var flags)
list(APPEND compile_options "/arch:AVX2")
endif()
endif()
if (LIBC_TARGET_ARCHITECTURE_IS_GPU)
list(APPEND compile_options "-fopenmp")
list(APPEND compile_options "-fopenmp-cuda-mode")
foreach(gpu_arch ${LIBC_GPU_ARCHITECTURES})
list(APPEND compile_options "--offload-arch=${gpu_arch}")
endforeach()
list(APPEND compile_options "-nogpulib")
list(APPEND compile_options "-nogpuinc")
list(APPEND compile_options "-fvisibility=hidden")
list(APPEND compile_options "-foffload-lto")
endif()
set(${output_var} ${compile_options} PARENT_SCOPE)
endfunction()

Expand Down
18 changes: 18 additions & 0 deletions libc/config/gpu/api.td
@@ -0,0 +1,18 @@
include "config/public_api.td"

include "spec/stdc.td"

def NullMacro : MacroDef<"NULL"> {
let Defn = [{
#define __need_NULL
#include <stddef.h>
}];
}

def StringAPI : PublicAPI<"string.h"> {
let Types = ["size_t"];

let Macros = [
NullMacro,
];
}
56 changes: 56 additions & 0 deletions libc/config/gpu/entrypoints.txt
@@ -0,0 +1,56 @@
set(TARGET_LIBC_ENTRYPOINTS
# ctype.h entrypoints
libc.src.ctype.isalnum
libc.src.ctype.isalpha
libc.src.ctype.isascii
libc.src.ctype.isblank
libc.src.ctype.iscntrl
libc.src.ctype.isdigit
libc.src.ctype.isgraph
libc.src.ctype.islower
libc.src.ctype.isprint
libc.src.ctype.ispunct
libc.src.ctype.isspace
libc.src.ctype.isupper
libc.src.ctype.isxdigit
libc.src.ctype.toascii
libc.src.ctype.tolower
libc.src.ctype.toupper

# string.h entrypoints
libc.src.string.bcmp
libc.src.string.bzero
libc.src.string.memccpy
libc.src.string.memchr
libc.src.string.memcmp
libc.src.string.memcpy
libc.src.string.memmove
libc.src.string.mempcpy
libc.src.string.memrchr
libc.src.string.memset
libc.src.string.stpcpy
libc.src.string.stpncpy
libc.src.string.strcat
libc.src.string.strchr
libc.src.string.strcmp
libc.src.string.strcpy
libc.src.string.strcspn
libc.src.string.strlcat
libc.src.string.strlcpy
libc.src.string.strlen
libc.src.string.strncat
libc.src.string.strncmp
libc.src.string.strncpy
libc.src.string.strnlen
libc.src.string.strpbrk
libc.src.string.strrchr
libc.src.string.strspn
libc.src.string.strstr
libc.src.string.strtok
libc.src.string.strtok_r
)

set(TARGET_LLVMLIBC_ENTRYPOINTS
${TARGET_LIBC_ENTRYPOINTS}
)

4 changes: 4 additions & 0 deletions libc/config/gpu/headers.txt
@@ -0,0 +1,4 @@
set(TARGET_PUBLIC_HEADERS
libc.include.ctype
libc.include.string
)
14 changes: 13 additions & 1 deletion libc/src/__support/architectures.h
Expand Up @@ -9,7 +9,19 @@
#ifndef LLVM_LIBC_SUPPORT_ARCHITECTURES_H
#define LLVM_LIBC_SUPPORT_ARCHITECTURES_H

#if defined(__pnacl__) || defined(__CLR_VER)
#if defined(__AMDGPU__)
#define LLVM_LIBC_ARCH_AMDGPU
#endif

#if defined(__NVPTX__)
#define LLVM_LIBC_ARCH_NVPTX
#endif

#if defined(LLVM_LIBC_ARCH_NVPTX) || defined(LLVM_LIBC_ARCH_AMDGPU)
#define LLVM_LIBC_ARCH_GPU
#endif

#if defined(__pnacl__) || defined(__CLR_VER) || defined(LLVM_LIBC_ARCH_GPU)
#define LLVM_LIBC_ARCH_VM
#endif

Expand Down
14 changes: 13 additions & 1 deletion libc/src/__support/common.h
Expand Up @@ -25,8 +25,20 @@
#define LLVM_LIBC_FUNCTION_ATTR
#endif

// We use OpenMP to declare these functions on the device.
#define STR(X) #X
#define LLVM_LIBC_DECLARE_DEVICE(name) \
_Pragma(STR(omp declare target to(name) device_type(nohost)))

// GPU targets do not support aliasing and must be declared on the device.
#if defined(LLVM_LIBC_PUBLIC_PACKAGING) && defined(_OPENMP)
#define LLVM_LIBC_FUNCTION(type, name, arglist) \
LLVM_LIBC_FUNCTION_ATTR decltype(__llvm_libc::name) \
__##name##_impl__ __asm__(#name); \
LLVM_LIBC_DECLARE_DEVICE(__##name##_impl__) \
type __##name##_impl__ arglist
// MacOS needs to be excluded because it does not support aliasing.
#if defined(LLVM_LIBC_PUBLIC_PACKAGING) && (!defined(__APPLE__))
#elif defined(LLVM_LIBC_PUBLIC_PACKAGING) && (!defined(__APPLE__))
#define LLVM_LIBC_FUNCTION(type, name, arglist) \
LLVM_LIBC_FUNCTION_ATTR decltype(__llvm_libc::name) \
__##name##_impl__ __asm__(#name); \
Expand Down
2 changes: 2 additions & 0 deletions libc/src/string/memory_utils/bcmp_implementations.h
Expand Up @@ -166,6 +166,8 @@ static inline BcmpReturnType inline_bcmp(CPtr p1, CPtr p2, size_t count) {
return inline_bcmp_aarch64(p1, p2, count);
#elif defined(LLVM_LIBC_ARCH_ARM)
return inline_bcmp_embedded_tiny(p1, p2, count);
#elif defined(LLVM_LIBC_ARCH_GPU)
return inline_bcmp_embedded_tiny(p1, p2, count);
#else
#error "Unsupported platform"
#endif
Expand Down
2 changes: 2 additions & 0 deletions libc/src/string/memory_utils/memcmp_implementations.h
Expand Up @@ -138,6 +138,8 @@ static inline MemcmpReturnType inline_memcmp(CPtr p1, CPtr p2, size_t count) {
#endif
#elif defined(LLVM_LIBC_ARCH_ARM)
return inline_memcmp_embedded_tiny(p1, p2, count);
#elif defined(LLVM_LIBC_ARCH_GPU)
return inline_memcmp_embedded_tiny(p1, p2, count);
#else
#error "Unsupported platform"
#endif
Expand Down
2 changes: 2 additions & 0 deletions libc/src/string/memory_utils/memcpy_implementations.h
Expand Up @@ -126,6 +126,8 @@ static inline void inline_memcpy(Ptr __restrict dst, CPtr __restrict src,
return inline_memcpy_aarch64(dst, src, count);
#elif defined(LLVM_LIBC_ARCH_ARM)
return inline_memcpy_embedded_tiny(dst, src, count);
#elif defined(LLVM_LIBC_ARCH_GPU)
return inline_memcpy_embedded_tiny(dst, src, count);
#else
#error "Unsupported platform"
#endif
Expand Down
2 changes: 2 additions & 0 deletions libc/src/string/memory_utils/memmove_implementations.h
Expand Up @@ -103,6 +103,8 @@ static inline void inline_memmove(Ptr dst, CPtr src, size_t count) {
}
#elif defined(LLVM_LIBC_ARCH_ARM)
return inline_memmove_embedded_tiny(dst, src, count);
#elif defined(LLVM_LIBC_ARCH_GPU)
return inline_memmove_embedded_tiny(dst, src, count);
#else
#error "Unsupported platform"
#endif
Expand Down
2 changes: 2 additions & 0 deletions libc/src/string/memory_utils/memset_implementations.h
Expand Up @@ -106,6 +106,8 @@ inline static void inline_memset(Ptr dst, uint8_t value, size_t count) {
return inline_memset_aarch64<kMaxSize>(dst, value, count);
#elif defined(LLVM_LIBC_ARCH_ARM)
return inline_memset_embedded_tiny(dst, value, count);
#elif defined(LLVM_LIBC_ARCH_GPU)
return inline_memset_embedded_tiny(dst, value, count);
#else
#error "Unsupported platform"
#endif
Expand Down
2 changes: 1 addition & 1 deletion libc/utils/CMakeLists.txt
Expand Up @@ -2,7 +2,7 @@ add_subdirectory(MPFRWrapper)
add_subdirectory(testutils)
add_subdirectory(UnitTest)

if(LLVM_LIBC_FULL_BUILD)
if(LLVM_LIBC_FULL_BUILD AND NOT LIBC_TARGET_ARCHITECTURE_IS_GPU)
add_subdirectory(IntegrationTest)
add_subdirectory(tools)
endif()

0 comments on commit 55151e1

Please sign in to comment.