diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.4.3) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") + +# The top-level source directory of libc. +set(LIBC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +# The top-level directory in which libc is being built. +set(LIBC_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +# Path libc/scripts directory. +set(LIBC_SOURCE_SCRIPTS_DIR "${LIBC_SOURCE_DIR}/scripts") + + +set(LIBC_TARGET_OS ${CMAKE_SYSTEM_NAME}) +string(TOLOWER ${LIBC_TARGET_OS} LIBC_TARGET_OS) + +set(LIBC_TARGET_MACHINE ${CMAKE_SYSTEM_PROCESSOR}) + +set(AR "ar") + +include(CMakeParseArguments) +include(LLVMLibCRules) + +add_subdirectory(include) +add_subdirectory(src) +add_subdirectory(lib) diff --git a/libc/cmake/modules/LLVMLibCRules.cmake b/libc/cmake/modules/LLVMLibCRules.cmake new file mode 100644 --- /dev/null +++ b/libc/cmake/modules/LLVMLibCRules.cmake @@ -0,0 +1,214 @@ + +# A rule for self contained header file targets. +# This rule merely copies the header file from the current source directory to +# the current binary directory. +# Usage: +# add_header( +# +# HDR
+# ) +function(add_header target_name) + cmake_parse_arguments( + "ADD_HEADER" + "" # No optional arguments + "HDR" # Single value arguments + "" # No multi value arguments + ${ARGN} + ) + if(NOT ADD_HEADER_HDR) + message(FATAL_ERROR "'add_header' rules requires the HDR argument specifying a headef file.") + endif() + + set(dest_file ${CMAKE_CURRENT_BINARY_DIR}/${ADD_HEADER_HDR}) + set(src_file ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_HEADER_HDR}) + + add_custom_command( + OUTPUT ${dest_file} + COMMAND cp ${src_file} ${dest_file} + DEPENDS ${src_file} + ) + + add_custom_target( + ${target_name} + DEPENDS ${dest_file} + ) +endfunction(add_header) + +# A rule for generated header file targets. +# Usage: +# add_gen_header( +# +# DEF_FILE <.h.def file> +# GEN_HDR +# PARAMS +# DATA_FILES +# ) +function(add_gen_header target_name) + cmake_parse_arguments( + "ADD_GEN_HDR" + "" # No optional arguments + "DEF_FILE;GEN_HDR" # Single value arguments + "PARAMS;DATA_FILES" # Multi value arguments + ${ARGN} + ) + if(NOT ADD_GEN_HDR_DEF_FILE) + message(FATAL_ERROR "`add_gen_hdr` rule requires DEF_FILE to be specified.") + endif() + if(NOT ADD_GEN_HDR_GEN_HDR) + message(FATAL_ERROR "`add_gen_hdr` rule requires GEN_HDR to be specified.") + endif() + + set(out_file ${CMAKE_CURRENT_BINARY_DIR}/${ADD_GEN_HDR_GEN_HDR}) + set(in_file ${CMAKE_CURRENT_SOURCE_DIR}/${ADD_GEN_HDR_DEF_FILE}) + + set(fq_data_files "") + if(ADD_GEN_HDR_DATA_FILES) + foreach(data_file IN LISTS ADD_GEN_HDR_DATA_FILES) + list(APPEND fq_data_files "${CMAKE_CURRENT_SOURCE_DIR}/${data_file}") + endforeach(data_file) + endif() + + set(replacement_params "") + if(ADD_GEN_HDR_PARAMS) + list(APPEND replacement_params "-P" ${ADD_GEN_HDR_PARAMS}) + endif() + + set(gen_hdr_script "${LIBC_SOURCE_SCRIPTS_DIR}/gen_hdr.py") + + add_custom_command( + OUTPUT ${out_file} + COMMAND ${gen_hdr_script} -o ${out_file} ${in_file} ${replacement_params} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${in_file} ${fq_data_files} ${gen_hdr_script} + ) + + add_custom_target( + ${target_name} + DEPENDS ${out_file} + ) +endfunction(add_gen_header) + +set(ENTRYPOINT_OBJ_TARGET_TYPE "ENTRYPOINT_OBJ") + +# A rule for entrypoint object targets. +# Usage: +# add_entrypoint_object( +# +# SRCS +# HDRS +# DEPENDS +# ) +function(add_entrypoint_object target_name) + cmake_parse_arguments( + "ADD_ENTRYPOINT_OBJ" + "" # No optional arguments + "" # No single value arguments + "SRCS;HDRS;DEPENDS" # Multi value arguments + ${ARGN} + ) + if(NOT ADD_ENTRYPOINT_OBJ_SRCS) + message(FATAL_ERROR "`add_entrypoint_object` rule requires SRCS to be specified.") + endif() + if(NOT ADD_ENTRYPOINT_OBJ_HDRS) + message(FATAL_ERROR "`add_entrypoint_object` rule requires HDRS to be specified.") + endif() + + add_library( + "${target_name}_objects" + # We want an object library as the objects will eventually get packaged into + # an archive (like libc.a). + OBJECT + ${ADD_ENTRYPOINT_OBJ_SRCS} + ${ADD_ENTRYPOINT_OBJ_HDRS} + ) + target_compile_options( + ${target_name}_objects + BEFORE + PRIVATE + -nostdinc -fpie -std=c++11 + ) + target_include_directories( + ${target_name}_objects + PRIVATE + "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_DIR};${SYSTEM_HEADER_DIR}" + ) + add_dependencies( + ${target_name}_objects + support_common_h + ) + if(ADD_ENTRYPOINT_OBJ_DEPENDS) + add_dependencies( + ${target_name}_objects + ${ADD_ENTRYPOINT_OBJ_DEPENDS} + ) + endif() + + set(object_file_raw "${CMAKE_CURRENT_BINARY_DIR}/${target_name}_raw.o") + set(object_file "${CMAKE_CURRENT_BINARY_DIR}/${target_name}.o") + + add_custom_command( + OUTPUT ${object_file_raw} + DEPENDS $ + COMMAND ld -r $ -o ${object_file_raw} + ) + + add_custom_command( + OUTPUT ${object_file} + DEPENDS ${object_file_raw} llvm-objcopy + COMMAND $ --add-symbol "${target_name}=.llvm.libc.entrypoint.${target_name}:0,function" ${object_file_raw} ${object_file} + ) + + add_custom_target( + ${target_name} + ALL + DEPENDS ${object_file} + ) + set_target_properties( + ${target_name} + PROPERTIES + "TARGET_TYPE" ${ENTRYPOINT_OBJ_TARGET_TYPE} + "OBJECT_FILE" ${object_file} + ) +endfunction(add_entrypoint_object) + +# A rule to build a library from a collection of entrypoint objects. +# Usage: +# add_entrypoint_library( +# DEPENDS +# ) +function(add_entrypoint_library target_name) + cmake_parse_arguments( + "ENTRYPOINT_LIBRARY" + "" # No optional arguments + "" # No single value arguments + "DEPENDS" # Multi-value arguments + ${ARGN} + ) + if(NOT ENTRYPOINT_LIBRARY_DEPENDS) + message(FATAL_ERROR "'add_entrypoint_library' target requires a DEPENDS list of 'add_entrypoint_object' targets.") + endif() + + set(obj_list "") + foreach(dep IN LISTS ENTRYPOINT_LIBRARY_DEPENDS) + get_target_property(dep_type ${dep} "TARGET_TYPE") + string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint) + if(NOT dep_is_entrypoint) + message(FATAL_ERROR "Dependency '${dep}' of 'add_entrypoint_collection' is not an 'add_entrypoint_object' target.") + endif() + get_target_property(target_obj_file ${dep} "OBJECT_FILE") + list(APPEND obj_list "${target_obj_file}") + endforeach(dep) + + set(library_file "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}${target_name}${CMAKE_STATIC_LIBRARY_SUFFIX}") + add_custom_command( + OUTPUT ${library_file} + COMMAND ${AR} -r ${library_file} ${obj_list} + DEPENDS ${obj_list} + ) + add_custom_target( + ${target_name} + ALL + DEPENDS ${library_file} + ) +endfunction(add_entrypoint_library) + diff --git a/libc/docs/build_system.md b/libc/docs/build_system.md new file mode 100644 --- /dev/null +++ b/libc/docs/build_system.md @@ -0,0 +1,24 @@ +LLVM libc build rules +===================== + +At the cost of verbosity, we want to keep the build system of LLVM libc +as simple as possible. We also want to be highly modular with our build +targets. This makes picking and choosing desired pieces a straighforward +task. + +Targets for entrypoints +======================= + +Every [enrtypoint](entrypoint.md) in LLVM-libc has its own build target. This +target is listed using the `add_entrypoint_object` rule. This rule generates +a single object file containing the implementation of the entrypoint. + +Targets for entrypoint libraries +================================ + +Standards like POSIX require that a libc provide certain library files like +`libc.a`, `libm.a`, etc. The targets for such library files are listed in the +`lib` directory as `add_entrypoint_library` targets. An `add_entrypoint_library` +target takes a list of `add_entrypoint_object` targets and produces a static +library containing the object files corresponding to the +`add_entrypoint_targets`. diff --git a/libc/docs/entrypoints.md b/libc/docs/entrypoints.md new file mode 100644 --- /dev/null +++ b/libc/docs/entrypoints.md @@ -0,0 +1,5 @@ +# Entrypoints in LLVM libc + +A public function or a global variable provided by LLVM-libc is called an +entrypoint. The notion of entrypoints is ingrained LLVM-libc's +[source layout](source_layout.md), [build system](build_system.md) etc. diff --git a/libc/docs/header_generation.md b/libc/docs/header_generation.md new file mode 100644 --- /dev/null +++ b/libc/docs/header_generation.md @@ -0,0 +1,57 @@ +# Generating Public and Internal headers + +Other libc implementations make use of preprocessor macro tricks to make header +files platform agnostic. When macros aren't suitable, they rely on build +system tricks to pick the right set of files to compile and export. While these +approaches have served them well, parts of their systems have become extremely +complicated making it hard to modify, extend or maintain. To avoid these +problems in llvm-libc, we use a header generation mechanism. The mechanism is +driven by a *header configuration language*. + +# Header Configuration Language + +Header configuration language consists of few special *replacement commands*. +These are listed in a normal header file, but with a rule that the extension of +the file containing replacment commands should be `.h.def` instead of the normal +`.h` extension. A header generation tool reads the `.h.def` files and produces +a `.h` file. This tool does two things: + +1. Copy the lines not containing replacement commands as is into the output +`.h` file. +2. Replace the line on which a replacement command occurs with some other +text as directed by the command. The replacment text can span multiple +lines. + +## Replacment command syntax + +A replacement command should be listed on a line by itself, and should not span +more than one line. The first token to appear on the line is the command name +prefixed with `%%`. For example, a line with the `include_file` command should +start with `%%include_file`. There can be indentation spaces before the `%%` +prefix. + +Replacement commands typically take arguments. They are essentially named +parameters specified in a comma separated list within parenthesis, similar to +the C function call syntax. Before performing the action corresponding to the +command, the header generator replaces the parameters with a concrete values. + +## Parameter Syntax + +Parameters are named indentifiers but prefixed with `$` and enclosed in `{` and +`}`. For example, `${path_to_constants}`. + +# Available Replacement Commands + +Sub-sections below describe the replacement commands currently available. + +## `include_file` + +### Arguments + +* **path parameter** - A parameter representing a path to a file. + +### Action + +This command instructs that the line on which the command appears should be +replaced by the contents of the file whose path is passed as argument to the +command. diff --git a/libc/docs/implementation_standard.md b/libc/docs/implementation_standard.md new file mode 100644 --- /dev/null +++ b/libc/docs/implementation_standard.md @@ -0,0 +1,78 @@ +# Convention for implementing entrypoints + +LLVM-libc entrypoints are defined [here](entrypoints.md). In this document, +we explain how the entrypoints are implemented. The +[source layout](source_layout.md) document explains that, within the high level +`src` directory, there exists one directory for every public header file +provided by LLVM-libc. The implementation of each entrypoint will also live in a +directory of its own. This directory will have the same name as that of the +entrypoint, and will be under the directory corresponding to its header file. +For example, the implementation of the `round` function from `math.h` will in +the directory `src/math/round/`. Within this directory, the implementation can +span multiple `.cpp` and `.h` files, but there will be atleast one header file +with name of the form `.h`. This header file is called as the +implementation header file. For the `round` function, the path to the +implementation header file will be `src/math/round/round.h`. The rest of this +document explains the structure of implementation header files and `.cpp` files. + +## Implementaion Header File Structure + +We will use the `round` function from the public `math.h` header file as an +example. The `round` function will be declared in an internal header file +`src/math/round/round.h` as follows: + +``` +// --- round.h --- // +#ifndef LLVM_LIBC_SRC_MATH_ROUND_ROUND_H +#define LLVM_LIBC_SRC_MATH_ROUND_ROUND_H + +namespace llvm_libc { + +double round(double); + +} // namespace llvm_libc + +#endif LLVM_LIBC_SRC_MATH_ROUND_ROUND_H +``` +Notice that the `round` function declaration is nested inside the namespace +`llvm_libc`. All implementation constructs in LLVM-libc are declared within +the namespace `llvm_libc`. + +## `.cpp` File Structure + +The implementation can span multiple `.cpp` files. However, the signature of +the entrypoint function should make use of a special macro. For example, the +`round` function from `math.h` should be defined as follows, say in the file +`src/math/math/round.cpp`: + +``` +// --- round.cpp --- // + +namespace llvm_libc { + +double LLVM_LIBC_ENTRYPOINT(round)(double d) { + // ... implementation goes here. +} + +} // namespace llvm_libc +``` + +Notice the use of the macro `LLVM_LIBC_ENTRYPOINT`. This macro helps us define +an C alias symbol for the C++ implementation. The C alias need not be added by +the macro by itself. For example, for ELF targets, the macro is defined as +follows: + +``` +#define ENTRYPOINT_SECTION_ATTRIBUTE(name) \ + __attribute__((section(".llvm.libc.entrypoint."#name))) +#define LLVM_LIBC_ENTRYPOINT(name) ENTRYPOINT_SECTION_ATTRIBUTE(name) name +``` + +The macro places the C++ function in a unique section with name +`.llvm.libc.entrypoint.`. This allows us to add a C alias using +a post build step. For example, for the `round` function, one can use `objcopy` +to add an alias symbol as follows: + +``` +objcopy --add-symbol round=.llvm.libc.entrypoint.round:0,function [libround.a | round.o] +``` diff --git a/libc/docs/source_layout.md b/libc/docs/source_layout.md new file mode 100644 --- /dev/null +++ b/libc/docs/source_layout.md @@ -0,0 +1,76 @@ +# LLVM-libc Source Tree Layout + +At the top-level, LLVM-libc source tree is organized in to the following +directories: + +``` ++ libc + - cmake + - docs + - include + - lib + - loader + - scripts + - src + - testing + - www +``` + +Each of these directories is explained in detail below. + +## The `cmake` directory + +The `cmake` directory contains the implementations of LLVM-libc's CMake build +rules. + +## The `docs` directory + +The `docs` directory contains design docs and also informative documents like +this document on source layout. + +## The `include` directory + +The `include` directory contains: + +1. **Self contained public header files** - These are header files which are +already in the form that get installed when LLVM-libc is installed on a user's +computer. +2. **`*.h.def` and `*.h.in` files** - These files are used to construct the the +generated public header files. +3. **A `CMakeLists.txt` file** - This file lists the targets for the self +contained and generated public header files. + +## The `lib` directory + +This directory contains a `CMakeLists.txt` file listing the targets for the +public libraries `libc.a`, `libm.a` etc. + +## The `loader` directory + +This directory contains the implementations of the application loaders like +`crt1.o` etc. + +## The `scripts` directory + +This directory contains scripts which support the build system, tooling etc. + +## The `src` directory + +This directory contains the implementations of the llvm-libc +[entrypoints](entrypoints.md). It is further organized as follows: + +1. There is a toplevel CMakeLists.txt file. +2. For every public header file provided by llvm-libc, there exists a +corresponding directory in the `src` directory. The name of the directory is +same as the base name of the header file. For example, the directory +corresponding to the public `math.h` header file is named `math`. See the +[implementation standard](implementation_standard.md) for information about +these *header* directories. + +## The `testing` directory + +The `testing` directory contains testing infrastructure. + +## The `www` directory + +The `www` directory contains the HTML content of libc.llvm.org diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/include/CMakeLists.txt @@ -0,0 +1,32 @@ + +add_header( + ctype_h + HDR + ctype.h +) + +add_header( + math_h + HDR + math.h +) + +add_header( + stdbool_h + HDR + stdbool.h +) + +add_gen_header( + string_h + PUBLIC + DEF_FILE string.h.def + GEN_HDR string.h + PARAMS + size_t_definition=gcc_clang_size_t.h.in + null_definition=null.h.in + DATA_FILES + gcc_clang_size_t.h.in + null.h.in +) + diff --git a/libc/include/ctype.h b/libc/include/ctype.h new file mode 100644 --- /dev/null +++ b/libc/include/ctype.h @@ -0,0 +1,48 @@ +//===---------------- C standard library header ctype.h -----------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_CTYPE_H +#define LLVM_LIBC_CTYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +int isalnum(int c); + +int isalpha(int c); + +int isblank(int c); + +int iscntrl(int c); + +int isdigit(int c); + +int isgraph(int c); + +int islower(int c); + +int isprint(int c); + +int ispunct(int c); + +int isspace(int c); + +int isupper(int c); + +int isxdigit(int c); + +int tolower(int c); + +int toupper(int c); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LLVM_LIBC_CTYPE_H diff --git a/libc/include/gcc_clang_size_t.h.in b/libc/include/gcc_clang_size_t.h.in new file mode 100644 --- /dev/null +++ b/libc/include/gcc_clang_size_t.h.in @@ -0,0 +1,20 @@ +//===------- Definition of size_t for gcc and clang compilations --------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Standard sepcifies that size_t be declared in stddef.h and string.h. So, we +// provide a single definition it in a separate .h.in file. This can be included +// at header generation time in stddef.h and string.h. +// +//===----------------------------------------------------------------------===// + +%%begin() + +#ifndef LLVM_LIBC_SIZE_T +#define LLVM_LIBC_SIZE_T +typedef __SIZE_TYPE__ size_t; +#endif // LLVM_LIBC_SIZE_T diff --git a/libc/include/math.h b/libc/include/math.h new file mode 100644 --- /dev/null +++ b/libc/include/math.h @@ -0,0 +1,362 @@ +//===----------------- C standard library header math.h -----------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_MATH_H +#define LLVM_LIBC_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +double acos(double x); + +float acosf(float x); + +long double acosl(long double x); + +double asin(double x); + +float asinf(float x); + +long double asinl(long double x); + +double atan(double x); + +float atanf(float x); + +long double atanl( long double x); + +double atan2(double y, double x); + +float atan2f(float y, float x); + +long double atan2l(long double y, long double x); + +double cos(double x); + +float cosf(float x); + +long double cosl(long double x); + +double sin(double x); + +float sinf(float x); + +long double sinl(long double x); + +double tan(double x); + +float tanf(float x); + +long double tanl(long double x); + +double acosh(double x); + +float acoshf(float x); + +long double acoshl(long double x); + +double asinh(double x); + +float asinhf(float x); + +long double asinhl(long double x); + +double atanh(double x); + +float atanhf(float x); + +long double atanhl(long double x); + +double cosh(double x); + +float coshf(float x); + +long double coshl(long double x); + +double sinh(double x); + +float sinhf(float x); + +long double sinhl(long double x); + +double tanh(double x); + +float tanhf(float x); + +long double tanhl(long double x); + +double exp(double x); + +float expf(float x); + +long double expl(long double x); + +double exp2(double x); + +float exp2f(float x); + +long double exp2l(long double x); + +double expm1(double x); + +float expm1f(float x); + +long double expm1l(long double x); + +double frexp(double x, int *exp); + +float frexpf(float x, int *exp); + +long double frexpl(long double x, int *exp); + +int ilogb(double x); + +int ilogbf(float x); + +int ilogbl(long double x); + +double ldexp(double x, int exp); + +float ldexpf(float x, int exp); + +long double ldexpl(long double x, int exp); + +double log(double x); + +float logf(float x); + +long double logl(long double x); + +double log10(double x); + +float log10f(float x); + +long double log10l(long double x); + +double log1p(double x); + +float log1pf(float x); + +long double log1pl(long double x); + +double log2(double x); + +float log2f(float x); + +long double log2l(long double x); + +double logb(double x); + +float logbf(float x); + +long double logbl(long double x); + +double modf(double x, double *iptr); + +float modff(float x, float *iptr); + +long double modfl(long double x, long double *iptr); + +double scalbn(double x, int exp); + +float scalbnf(float x, int exp); + +long double scalbnl(long double x, int exp); + +double scalbln(double x, long int exp); + +float scalblnf(float x, long int exp); + +long double scalblnl(long double x, long int exp); + +double cbrt(double x); + +float cbrtf(float x); + +long double cbrtl(long double x); + +double fabs(double x); + +float fabsf(float x); + +long double fabsl(long double x); + +double hypot(double x, double y); + +float hypotf(float x, float y); + +long double hypotl(long double x, long double y); + +double pow(double x, double y); + +float powf(float x, float y); + +long double powl(long double x, long double y); + +double sqrt(double x); + +float sqrtf(float x); + +long double sqrtl(long double x); + +double erf(double x); + +float erff(float x); + +long double erfl(long double x); + +double erfc(double x); + +float erfcf(float x); + +long double erfcl(long double x); + +double lgamma(double x); + +float lgammaf(float x); + +long double lgammal(long double x); + +double tgamma(double x); + +float tgammaf(float x); + +long double tgammal(long double x); + +double ceil(double x); + +float ceilf(float x); + +long double ceill(long double x); + +double floor(double x); + +float floorf(float x); + +long double floorl(long double x); + +double nearbyint(double x); + +float nearbyintf(float x); + +long double nearbyintl(long double x); + +double rint(double x); + +float rintf(float x); + +long double rintl(long double x); + +long int lrint(double x); + +long int lrintf(float x); + +long int lrintl(long double x); + +long long int llrint(double x); + +long long int llrintf(float x); + +long long int llrintl(long double x); + +double round(double x); + +float roundf(float x); + +long double roundl(long double x); + +long int lround(double x); + +long int lroundf(float x); + +long int lroundl(long double x); + +long long int llround(double x); + +long long int llroundf(float x); + +long long int llroundl(long double x); + +double trunc(double x); + +float truncf(float x); + +long double truncl(long double x); + +double fmod(double x, double y); + +float fmodf(float x, float y); + +long double fmodl(long double x, long double y); + +double remainder(double x, double y); + +float remainderf(float x, float y); + +long double remainderl(long double x, long double y); + +double remquo(double x, double y, int *quo); + +float remquof(float x, float y, int *quo); + +long double remquol(long double x, long double y, int *quo); + +double copysign(double x, double y); + +float copysignf(float x, float y); + +long double copysignl(long double x, long double y); + +double nan(const char *tagp); + +float nanf(const char *tagp); + +long double nanl(const char *tagp); + +double nextafter(double x, double y); + +float nextafterf(float x, float y); + +long double nextafterl(long double x, long double y); + +double nexttoward(double x, long double y); + +float nexttowardf(float x, long double y); + +long double nexttowardl(long double x, long double y); + +double fdim(double x, double y); + +float fdimf(float x, float y); + +long double fdiml(long double x, long double y); + +double fmax(double x, double y); + +float fmaxf(float x, float y); + +long double fmaxl(long double x, long double y); + +double fmin(double x, double y); + +float fminf(float x, float y); + +long double fminl(long double x, long double y); + +double fma(double x, double y, double z); + +float fmaf(float x, float y, float z); + +long double fmal(long double x, long double y, long double z); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LLVM_LIBC_MATH_H diff --git a/libc/include/null.h.in b/libc/include/null.h.in new file mode 100644 --- /dev/null +++ b/libc/include/null.h.in @@ -0,0 +1,23 @@ +//===------------------- Definition of the NULL macro ------------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Standard sepcifies that the macro NULL be defines in stddef.h and string.h. +// So, we provide a single definition it in a separate .h.in file. This can be +// included at header generation time in stddef.h and string.h. +// +//===----------------------------------------------------------------------===// + +%%begin() + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0L +#else +#define NULL ((void*)0) +#endif // __cplusplus +#endif // NULL diff --git a/libc/include/stdbool.h b/libc/include/stdbool.h new file mode 100644 --- /dev/null +++ b/libc/include/stdbool.h @@ -0,0 +1,22 @@ +//===---------------- C standard library header stdbool.h ---------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_STDBOOL_H +#define LLVM_LIBC_STDBOOL_H + +#ifndef __cplusplus + +#define bool _Bool +#define true 1 +#define false 0 + +#endif + +#define __bool_true_false_are_defined 1 + +#endif // LLVM_LIBC_STDBOOL_H diff --git a/libc/include/string.h.def b/libc/include/string.h.def new file mode 100644 --- /dev/null +++ b/libc/include/string.h.def @@ -0,0 +1,67 @@ +//===---------------- C standard library header string.h ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_STRING_H +#define LLVM_LIBC_STRING_H + +%%include_file(${size_t_definition}) +%%include_file(${null_definition}) + +#ifdef __cplusplus +extern "C" { +#endif + +void *memcpy(void *dest, const void *src, size_t n); + +void *memmove(void *dest, const void *src, size_t n); + +char *strcpy(char *dest, const char *src); + +char *strncpy(char *dest, const char *src, size_t n); + +char *strcat(char *dest, const char *src); + +char *strncat(char *dest, const char *src, size_t n); + +int memcmp(const void *s1, const void *s2, size_t n); + +int strcmp(const char *s1, const char *s2); + +int strcoll(const char *s1, const char *s2); + +int strncmp(const char *s1, const char *s2, size_t n); + +size_t strxfrm(char *dest, const char *src, size_t n); + +void *memchr(const void *s, int c, size_t n); + +char *strchr(const char *s, int c); + +size_t strcspn(const char *s, const char *reject); + +char *strpbrk(const char *s, const char *accept); + +char *strrchr(const char *s, int c); + +size_t strspn(const char *s, const char *accept); + +char *strstr(const char *haystack, const char *needle); + +char *strtok(char *str, const char *delim); + +void *memset(void *s, int c, size_t n); + +char *strerror(int errnum); + +size_t strlen(const char *s); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LLVM_LIBC_STRING_H diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/lib/CMakeLists.txt @@ -0,0 +1,9 @@ + +add_entrypoint_library( + llvmlibc + DEPENDS + # string.h entrypoints + strcpy + strcat +) + diff --git a/libc/scripts/gen_hdr.py b/libc/scripts/gen_hdr.py new file mode 100755 --- /dev/null +++ b/libc/scripts/gen_hdr.py @@ -0,0 +1,170 @@ +#! /usr/bin/python +#===---------------- Script to generate header files ----------------------===# +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https:#llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===-----------------------------------------------------------------------===# +# +# This script takes a .h.def file and generates a .h header file. +# See docs/header_generation.md for more information. +# +#===-----------------------------------------------------------------------===# + +import argparse +import os +import sys + +COMMAND_PREFIX = "%%" +BEGIN_COMMAND = "begin" +INCLUDE_FILE_COMMAND = "include_file" + + +class _Location(object): + def __init__(self, filename, line_number): + self.filename = filename + self.line_number = line_number + + def __str__(self): + return "%s:%s" % (self.filename, self.line_number) + + +class OutputStreamManager(object): + def __init__(self, filename): + self.filename = filename + + def __enter__(self): + if self.filename is None: + self.stream = sys.stdout + else: + self.stream = open(self.filename, "w") + return self.stream + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.filename is not None: + self.stream.close() + return False + + +def _parse_command(loc, line): + open_paren = line.find("(") + if open_paren < 0 or line[-1] != ")": + return _report_error(loc, "Incorrect replacement command syntax.") + command_name = line[len(COMMAND_PREFIX):open_paren] + args = line[open_paren + 1:-1].split(",") + args = [a.strip() for a in args] + return command_name.strip(), args + + +def _is_param(token): + if token.startswith("${") and token.endswith("}"): + return True + else: + return False + + +def _get_param_name(token): + return token[2:-1] + + +def _report_error(loc, msg): + sys.exit("ERROR:%s: %s" % (loc, msg)) + + +def _is_begin_command(line): + if line.startswith(COMMAND_PREFIX + BEGIN_COMMAND): + return True + + +def include_file_command(out_stream, loc, args, params): + if len(args) != 1: + _report_error(loc, "`%%include_file` command takes exactly one " + "argument. %d given." % len(args)) + include_file_path = args[0] + if _is_param(include_file_path): + param_name = _get_param_name(include_file_path) + include_file_path = params.get(param_name) + if not include_file_path: + _report_error( + loc, + "No value specified for parameter '%s'." % path_param) + if not os.path.exists(include_file_path): + _report_error( + loc, + "Include file %s not found." % include_file_path) + with open(include_file_path, "r") as include_file: + begin = False + for line in include_file.readlines(): + line = line.strip() + if _is_begin_command(line): + # Parse the command to make sure there are no errors. + command_name, args = _parse_command(loc, line) + begin = True + # Skip the line on which %%begin() is listed. + continue + if begin: + out_stream.write(line + "\n") + + +# Mapping from a command name to its implementation function. +REPLACEMENT_COMMANDS = { + INCLUDE_FILE_COMMAND: include_file_command, +} + + +def apply_replacement_command(out_stream, loc, line, params): + if not line.startswith(COMMAND_PREFIX): + # This line is not a replacement command. + return line + command_name, args = _parse_command(loc, line) + command = REPLACEMENT_COMMANDS.get(command_name.strip()) + if not command: + _report_error(loc, "Unknown replacement command `%`", command_name) + command(out_stream, loc, args, params) + + +def parse_command_line(): + parser = argparse.ArgumentParser( + description="Script to generate header files from .def files.") + parser.add_argument("def_file", metavar="DEF_FILE", + help="Path to the .def file.") + parser.add_argument("--params", "-P", nargs= "*", default=[], + help="NAME=VALUE pairs for replacement parameters in " + "the input .def file.") + # The output file argument is optional. If not specified, the generated + # header file content will be written to stdout. + parser.add_argument("--out-file", "-o", + help="Path to the generated header file. Defaults to " + "stdout") + opts = parser.parse_args() + if not all(["=" in param for param in opts.params]): + # We want all params to be specified in the form "name=value". + _report_error( + __file__ + ":" + "[command line]", + "Replacement parameters should be listed in the form NAME=VALUE") + return opts + + +def main(): + opts = parse_command_line() + param_dict = {} + for name_value_pair in opts.params: + name, value = name_value_pair.split("=") + param_dict[name] = value + with open(opts.def_file, "r") as def_file: + loc = _Location(opts.def_file, 0) + with OutputStreamManager(opts.out_file) as out_stream: + for line in def_file.readlines(): + loc.line_number += 1 + line = line.strip() + if line.startswith(COMMAND_PREFIX): + replacement_text = apply_replacement_command( + out_stream, loc, line, param_dict) + out_stream.write("\n") + else: + out_stream.write(line + "\n") + + +if __name__ == "__main__": + main() diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(string) + +add_subdirectory(__support) diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/__support/CMakeLists.txt @@ -0,0 +1,9 @@ +add_gen_header( + support_common_h + DEF_FILE common.h.def + PARAMS + entrypoint_macro=${LIBC_TARGET_OS}/entrypoint_macro.h.in + GEN_HDR common.h + DATA_FILES + ${LIBC_TARGET_OS}/entrypoint_macro.h.in +) diff --git a/libc/src/__support/common.h.def b/libc/src/__support/common.h.def new file mode 100644 --- /dev/null +++ b/libc/src/__support/common.h.def @@ -0,0 +1,16 @@ +//===-------------------- Common internal contructs ---------------------*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SUPPORT_COMMON_H +#define LLVM_LIBC_SUPPORT_COMMON_H + +#define INLINE_ASM __asm__ __volatile__ + +%%include_file(${entrypoint_macro}) + +#endif // LLVM_LIBC_SUPPORT_COMMON_H diff --git a/libc/src/__support/linux/entrypoint_macro.h.in b/libc/src/__support/linux/entrypoint_macro.h.in new file mode 100644 --- /dev/null +++ b/libc/src/__support/linux/entrypoint_macro.h.in @@ -0,0 +1,13 @@ +//===---- Definition of LLVM_LIBC_ENTRYPOINT macro for ELF paltforms ----*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +%%begin() + +#define ENTRYPOINT_SECTION_ATTRIBUTE(name) \ + __attribute__((section(".llvm.libc.entrypoint."#name))) +#define LLVM_LIBC_ENTRYPOINT(name) ENTRYPOINT_SECTION_ATTRIBUTE(name) name diff --git a/libc/src/string/CMakeLists.txt b/libc/src/string/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/string/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(strcpy) +add_subdirectory(strcat) diff --git a/libc/src/string/strcat/CMakeLists.txt b/libc/src/string/strcat/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/string/strcat/CMakeLists.txt @@ -0,0 +1,10 @@ +add_entrypoint_object( + strcat + SRCS + strcat.cpp + HDRS + strcat.h + DEPENDS + strcpy + string_h +) diff --git a/libc/src/string/strcat/strcat.h b/libc/src/string/strcat/strcat.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strcat/strcat.h @@ -0,0 +1,19 @@ +//===----------------- Implementation header for strcat -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STRING_STRCAT_H +#define LLVM_LIBC_SRC_STRING_STRCAT_H + +#include +namespace llvm_libc { + +char *strcat(char *dest, const char *src); + +} // namespace llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCAT_H diff --git a/libc/src/string/strcat/strcat.cpp b/libc/src/string/strcat/strcat.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strcat/strcat.cpp @@ -0,0 +1,22 @@ +//===-------------------- Implementation of strcat -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strcat/strcat.h" +#include "src/string/strcpy/strcpy.h" +#include "src/__support/common.h" + +namespace llvm_libc { + +char *LLVM_LIBC_ENTRYPOINT(strcat)(char *dest, const char *src) { + // We do not yet have an implementaion of strlen in so we will use strlen + // from another libc. + llvm_libc::strcpy(dest + ::strlen(dest), src); + return dest; +} + +} // namespace llvm_libc diff --git a/libc/src/string/strcpy/CMakeLists.txt b/libc/src/string/strcpy/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/string/strcpy/CMakeLists.txt @@ -0,0 +1,9 @@ +add_entrypoint_object( + strcpy + SRCS + strcpy.cpp + HDRS + strcpy.h + DEPENDS + string_h +) diff --git a/libc/src/string/strcpy/strcpy.h b/libc/src/string/strcpy/strcpy.h new file mode 100644 --- /dev/null +++ b/libc/src/string/strcpy/strcpy.h @@ -0,0 +1,19 @@ +//===----------------- Implementation header for strcpy -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_STRING_STRCPY_H +#define LLVM_LIBC_SRC_STRING_STRCPY_H + +#include +namespace llvm_libc { + +char *strcpy(char *dest, const char *src); + +} // namespace llvm_libc + +#endif // LLVM_LIBC_SRC_STRING_STRCPY_H diff --git a/libc/src/string/strcpy/strcpy.cpp b/libc/src/string/strcpy/strcpy.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strcpy/strcpy.cpp @@ -0,0 +1,19 @@ +//===-------------------- Implementation of strcpy -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/string/strcpy/strcpy.h" +#include "src/__support/common.h" + +namespace llvm_libc { + + +char *LLVM_LIBC_ENTRYPOINT(strcpy)(char *dest, const char *src) { + return reinterpret_cast(::memcpy(dest, src, ::strlen(src) + 1)); +} + +} // namespace llvm_libc diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -59,7 +59,7 @@ # LLVM_EXTERNAL_${project}_SOURCE_DIR using LLVM_ALL_PROJECTS # This allows an easy way of setting up a build directory for llvm and another # one for llvm+clang+... using the same sources. -set(LLVM_ALL_PROJECTS "clang;clang-tools-extra;compiler-rt;debuginfo-tests;libclc;libcxx;libcxxabi;libunwind;lld;lldb;llgo;openmp;parallel-libs;polly;pstl") +set(LLVM_ALL_PROJECTS "clang;clang-tools-extra;compiler-rt;debuginfo-tests;libc;libclc;libcxx;libcxxabi;libunwind;lld;lldb;llgo;openmp;parallel-libs;polly;pstl") set(LLVM_ENABLE_PROJECTS "" CACHE STRING "Semicolon-separated list of projects to build (${LLVM_ALL_PROJECTS}), or \"all\".") if( LLVM_ENABLE_PROJECTS STREQUAL "all" ) diff --git a/llvm/projects/CMakeLists.txt b/llvm/projects/CMakeLists.txt --- a/llvm/projects/CMakeLists.txt +++ b/llvm/projects/CMakeLists.txt @@ -31,6 +31,7 @@ # dependent projects can see the target names of their dependencies. add_llvm_external_project(libunwind) add_llvm_external_project(pstl) + add_llvm_external_project(libc) add_llvm_external_project(libcxxabi) add_llvm_external_project(libcxx) endif()