diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/CMakeLists.txt @@ -0,0 +1,24 @@ +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_BUILD_SCRIPTS_DIR "${LIBC_SOURCE_DIR}/utils/build_scripts") + + +set(LIBC_TARGET_OS ${CMAKE_SYSTEM_NAME}) +string(TOLOWER ${LIBC_TARGET_OS} LIBC_TARGET_OS) + +set(LIBC_TARGET_MACHINE ${CMAKE_SYSTEM_PROCESSOR}) + +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,273 @@ + +# 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_BUILD_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 + -fpie -std=${LLVM_CXX_STD_default} + ) + target_include_directories( + ${target_name}_objects + PRIVATE + "${LIBC_BUILD_DIR}/include;${LIBC_SOURCE_DIR};${LIBC_BUILD_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 ${CMAKE_LINKER} -r $ -o ${object_file_raw} + ) + + add_custom_command( + OUTPUT ${object_file} + DEPENDS ${object_file_raw} + COMMAND ${CMAKE_OBJCOPY} --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 ${CMAKE_AR} -r ${library_file} ${obj_list} + DEPENDS ${obj_list} + ) + add_custom_target( + ${target_name} + ALL + DEPENDS ${library_file} + ) +endfunction(add_entrypoint_library) + +function(add_libc_unittest target_name) + cmake_parse_arguments( + "LIBC_UNITTEST" + "" # No optional arguments + "SUITE" # Single value arguments + "SRCS;HDRS;DEPENDS" # Multi-value arguments + ${ARGN} + ) + if(NOT LIBC_UNITTEST_SRCS) + message(FATAL_ERROR "'add_libc_unittest' target requires a SRCS list of .cpp files.") + endif() + if(NOT LIBC_UNITTEST_DEPENDS) + message(FATAL_ERROR "'add_libc_unittest' target requires a DEPENDS list of 'add_entrypoint_object' targets.") + endif() + + set(entrypoint_objects "") + foreach(dep IN LISTS LIBC_UNITTEST_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_unittest' is not an 'add_entrypoint_object' target.") + endif() + get_target_property(obj_file ${dep} "OBJECT_FILE") + list(APPEND entrypoint_objects "${obj_file}") + endforeach(dep) + + add_executable( + ${target_name} + EXCLUDE_FROM_ALL + ${LIBC_UNITTEST_SRCS} + ${LIBC_UNITTEST_HDRS} + ) + target_include_directories( + ${target_name} + PRIVATE + ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include + ${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock/include + ${LIBC_SOURCE_DIR} + ) + target_link_libraries(${target_name} PRIVATE ${entrypoint_objects} gtest_main gtest) + set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_dependencies( + ${target_name} + ${LIBC_UNITTEST_DEPENDS} + gtest + ) + add_custom_command( + TARGET ${target_name} + POST_BUILD + COMMAND $ + ) + if(LIBC_UNITTEST_SUITE) + add_dependencies( + ${LIBC_UNITTEST_SUITE} + ${target_name} + ) + endif() +endfunction(add_libc_unittest) 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,89 @@ +# 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 *commands*. The header +generation mechanism takes a an input file, which has an extension of `.h.def,` +and produces a header file with `.h` extension. The header configuration +language commands are listed in the input `.h.def`file. While reading a `.h.def` +file, the header generation tool does two things: + +1. Copy the lines not containing commands as is into the output `.h` file. +2. Replace the line on which a command occurs with some other text as directed +by the command. The replacment text can span multiple lines. + +## Command syntax + +A 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. + +Most commands typically take arguments. They are listed as a comma separated +list of named identifiers within parenthesis, similar to the C function call +syntax. Before performing the action corresponding to the command, the header +generator replaces the arguments with concrete values. + +## Argument Syntax + +Arguments are named indentifiers but prefixed with `$` and enclosed in `{` and +`}`. For example, `${path_to_constants}`. + +## Comments + +There can be cases wherein one wants to add comments in the .h.def file but +does not want them to be copied into the generated header file. Such comments +can be added by beginning the comment lines with the `` prefix. Currently, +comments have to be on lines of their own. That is, they cannot be suffixes like +this: + +``` +%%include_file(a/b/c) Path to c in b of a. !!! WRONG SYNTAX +``` + +# Available Commands + +Sub-sections below describe the commands currently available. Under each command +is the discription of the arugments to the command, and the action taken by the +header generation tool when processing a command. + +## `include_file` + +This is a replacement command which should be listed in an input `.h.def` file. + +### Arguments + +* **path argument** - An argument representing a path to a file. The file +should have an extension of `.h.inc`. + +### 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. + +## `begin` + +This is not a replacement command. It is an error to list it in the input +`.h.def` file. It is normally listed in the files included by the `include_file` +command (the `.h.inc` files). A common use of this command it mark the beginning +of what is to be included. This prevents copying items like license headers into +the generated header file. + +### Arguments + +None. + +### Action + +The header generator will only include content starting from the line after the +line on which this command is listed. + 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,83 @@ +# 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 implementations of related group of entrypoints will +also live in a directory of their own. This directory will have a name +indicative of the related group of entrypoints, and will be under the directory +corresponding to the header file of the entrypoints. For example, functions like +`fopen` and `fclose` cannot be tested independent of each other and hence will +live in a directory named `src/stdio/file_operations`. On the other hand, the +implementation of the `round` function from `math.h` can be tested by itself, so +it will live in the directory of its own named `src/math/round/`. + +Implementation of entrypoints can span multiple `.cpp` and `.h` files, but there +will be atleast one header file with name of the form `.h` for +every entrypoint. 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 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,77 @@ +# 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 + - src + + utils + - build_scripts + - 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 `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 `www` directory + +The `www` directory contains the HTML content of libc.llvm.org + +## The `utils/build_scripts` directory + +This directory contains scripts which support the build system, tooling etc. + +## The `utils/testing` directory + +This directory contains testing infrastructure. 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,19 @@ + +add_header( + ctype_h + HDR + ctype.h +) + +add_header( + math_h + HDR + math.h +) + +add_header( + string_h + HDR + string.h +) + 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); + +int isalpha(int); + +int isblank(int); + +int iscntrl(int); + +int isdigit(int); + +int isgraph(int); + +int islower(int); + +int isprint(int); + +int ispunct(int); + +int isspace(int); + +int isupper(int); + +int isxdigit(int); + +int tolower(int); + +int toupper(int); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LLVM_LIBC_CTYPE_H 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); + +float acosf(float); + +long double acosl(long double); + +double asin(double); + +float asinf(float); + +long double asinl(long double); + +double atan(double); + +float atanf(float); + +long double atanl(long double); + +double atan2(double, double); + +float atan2f(float, float); + +long double atan2l(long double, long double); + +double cos(double); + +float cosf(float); + +long double cosl(long double); + +double sin(double); + +float sinf(float); + +long double sinl(long double); + +double tan(double); + +float tanf(float); + +long double tanl(long double); + +double acosh(double); + +float acoshf(float); + +long double acoshl(long double); + +double asinh(double); + +float asinhf(float); + +long double asinhl(long double); + +double atanh(double); + +float atanhf(float); + +long double atanhl(long double); + +double cosh(double); + +float coshf(float); + +long double coshl(long double); + +double sinh(double); + +float sinhf(float); + +long double sinhl(long double); + +double tanh(double); + +float tanhf(float); + +long double tanhl(long double); + +double exp(double); + +float expf(float); + +long double expl(long double); + +double exp2(double); + +float exp2f(float); + +long double exp2l(long double); + +double expm1(double); + +float expm1f(float); + +long double expm1l(long double); + +double frexp(double, int); + +float frexpf(float, int); + +long double frexpl(long double, int); + +int ilogb(double); + +int ilogbf(float); + +int ilogbl(long double); + +double ldexp(double, int); + +float ldexpf(float, int); + +long double ldexpl(long double, int); + +double log(double); + +float logf(float); + +long double logl(long double); + +double log10(double); + +float log10f(float); + +long double log10l(long double); + +double log1p(double); + +float log1pf(float); + +long double log1pl(long double); + +double log2(double); + +float log2f(float); + +long double log2l(long double); + +double logb(double); + +float logbf(float); + +long double logbl(long double); + +double modf(double, double); + +float modff(float, float); + +long double modfl(long double, long double); + +double scalbn(double, int); + +float scalbnf(float, int); + +long double scalbnl(long double, int); + +double scalbln(double, long int); + +float scalblnf(float, long int); + +long double scalblnl(long double, long int); + +double cbrt(double); + +float cbrtf(float); + +long double cbrtl(long double); + +double fabs(double); + +float fabsf(float); + +long double fabsl(long double); + +double hypot(double, double); + +float hypotf(float, float); + +long double hypotl(long double, long double); + +double pow(double, double); + +float powf(float, float); + +long double powl(long double, long double); + +double sqrt(double); + +float sqrtf(float); + +long double sqrtl(long double); + +double erf(double); + +float erff(float); + +long double erfl(long double); + +double erfc(double); + +float erfcf(float); + +long double erfcl(long double); + +double lgamma(double); + +float lgammaf(float); + +long double lgammal(long double); + +double tgamma(double); + +float tgammaf(float); + +long double tgammal(long double); + +double ceil(double); + +float ceilf(float); + +long double ceill(long double); + +double floor(double); + +float floorf(float); + +long double floorl(long double); + +double nearbyint(double); + +float nearbyintf(float); + +long double nearbyintl(long double); + +double rint(double); + +float rintf(float); + +long double rintl(long double); + +long int lrint(double); + +long int lrintf(float); + +long int lrintl(long double); + +long long int llrint(double); + +long long int llrintf(float); + +long long int llrintl(long double); + +double round(double); + +float roundf(float); + +long double roundl(long double); + +long int lround(double); + +long int lroundf(float); + +long int lroundl(long double); + +long long int llround(double); + +long long int llroundf(float); + +long long int llroundl(long double); + +double trunc(double); + +float truncf(float); + +long double truncl(long double); + +double fmod(double, double); + +float fmodf(float, float); + +long double fmodl(long double, long double); + +double remainder(double, double); + +float remainderf(float, float); + +long double remainderl(long double, long double); + +double remquo(double, double, int); + +float remquof(float, float, int); + +long double remquol(long double, long double, int); + +double copysign(double, double); + +float copysignf(float, float); + +long double copysignl(long double, long double); + +double nan(const char); + +float nanf(const char); + +long double nanl(const char); + +double nextafter(double, double); + +float nextafterf(float, float); + +long double nextafterl(long double, long double); + +double nexttoward(double, long double); + +float nexttowardf(float, long double); + +long double nexttowardl(long double, long double); + +double fdim(double, double); + +float fdimf(float, float); + +long double fdiml(long double, long double); + +double fmax(double, double); + +double fmaxf(double, double); + +double fmaxl(double, double); + +double fmin(double, double); + +float fminf(float, float); + +long double fminl(long double, long double); + +double fma(double, double, double); + +float fmaf(float, float, float); + +long double fmal(long double, long double, long double); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LLVM_LIBC_MATH_H diff --git a/libc/include/string.h b/libc/include/string.h new file mode 100644 --- /dev/null +++ b/libc/include/string.h @@ -0,0 +1,66 @@ +//===---------------- 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 // For size_t + +#ifdef __cplusplus +extern "C" { +#endif + +void *memcpy(void *, const void *, size_t); + +void *memmove(void *, const void *, size_t); + +int memcmp(const void *, const void *, size_t); + +void *memchr(const void *, int, size_t); + +void *memset(void *, int, size_t); + +char *strcpy(char *, const char *); + +char *strncpy(char *, const char *, size_t); + +char *strcat(char *, const char *); + +char *strncat(char *, const char *, size_t); + +int strcmp(const char *, const char *); + +int strcoll(const char *, const char *); + +int strncmp(const char *, const char *, size_t); + +size_t strxfrm(char *, const char *, size_t); + +char *strchr(const char *, int); + +size_t strcspn(const char *, const char *); + +char *strpbrk(const char *, const char *); + +char *strrchr(const char *, int c); + +size_t strspn(const char *, const char *); + +char *strstr(const char *, const char *); + +char *strtok(char *, const char *); + +char *strerror(int); + +size_t strlen(const char *); + +#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,10 @@ + +add_entrypoint_library( + llvmlibc + DEPENDS + ### C standard library entrypoints + # string.h entrypoints + strcpy + strcat +) + 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.inc + GEN_HDR common.h + DATA_FILES + ${LIBC_TARGET_OS}/entrypoint_macro.h.inc +) 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,18 @@ +//===-------------------- 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__ + + The entrypoint macro has a platform specific definition. So, we include the + right definition at build time. +%%include_file(${entrypoint_macro}) + +#endif // LLVM_LIBC_SUPPORT_COMMON_H diff --git a/libc/src/__support/linux/entrypoint_macro.h.inc b/libc/src/__support/linux/entrypoint_macro.h.inc new file mode 100644 --- /dev/null +++ b/libc/src/__support/linux/entrypoint_macro.h.inc @@ -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,4 @@ +add_custom_target(libc_string_unittests) + +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,21 @@ +add_entrypoint_object( + strcat + SRCS + strcat.cpp + HDRS + strcat.h + DEPENDS + strcpy + string_h +) + +add_libc_unittest( + strcat_test + SUITE + libc_string_unittests + SRCS + strcat_test.cpp + DEPENDS + strcat + strcpy +) 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,20 @@ +//===----------------- 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,23 @@ +//===-------------------- 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/__support/common.h" +#include "src/string/strcpy/strcpy.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/strcat/strcat_test.cpp b/libc/src/string/strcat/strcat_test.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strcat/strcat_test.cpp @@ -0,0 +1,43 @@ +//===---------------------- Unittests 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 +// +//===----------------------------------------------------------------------===// + +#include + +#include "src/string/strcat/strcat.h" +#include "gtest/gtest.h" + +TEST(StrCatTest, EmptyDest) { + std::string abc = "abc"; + char *dest = new char[4]; + + dest[0] = '\0'; + + char *result = __llvm_libc::strcat(dest, abc.c_str()); + ASSERT_EQ(dest, result); + ASSERT_EQ(std::string(dest), abc); + ASSERT_EQ(std::string(dest).size(), abc.size()); + + delete[] dest; +} + +TEST(StrCatTest, NonEmptyDest) { + std::string abc = "abc"; + char *dest = new char[4]; + + dest[0] = 'x'; + dest[1] = 'y'; + dest[2] = 'z'; + dest[3] = '\0'; + + char *result = __llvm_libc::strcat(dest, abc.c_str()); + ASSERT_EQ(dest, result); + ASSERT_EQ(std::string(dest), std::string("xyz") + abc); + ASSERT_EQ(std::string(dest).size(), abc.size() + 3); + + delete[] dest; +} 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,19 @@ +add_entrypoint_object( + strcpy + SRCS + strcpy.cpp + HDRS + strcpy.h + DEPENDS + string_h +) + +add_libc_unittest( + strcpy_test + SUITE + libc_string_unittests + SRCS + strcpy_test.cpp + DEPENDS + strcpy +) 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,20 @@ +//===----------------- 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/libc/src/string/strcpy/strcpy_test.cpp b/libc/src/string/strcpy/strcpy_test.cpp new file mode 100644 --- /dev/null +++ b/libc/src/string/strcpy/strcpy_test.cpp @@ -0,0 +1,40 @@ +//===----------------------- Unittests 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 +// +//===----------------------------------------------------------------------===// + +#include + +#include "src/string/strcpy/strcpy.h" +#include "gtest/gtest.h" + +TEST(StrCpyTest, EmptyDest) { + std::string abc = "abc"; + char *dest = new char[4]; + + char *result = __llvm_libc::strcpy(dest, abc.c_str()); + ASSERT_EQ(dest, result); + ASSERT_EQ(std::string(dest), abc); + ASSERT_EQ(std::string(dest).size(), abc.size()); + + delete[] dest; +} + +TEST(StrCpyTest, OffsetDest) { + std::string abc = "abc"; + char *dest = new char[7]; + + dest[0] = 'x'; + dest[1] = 'y'; + dest[2] = 'z'; + + char *result = __llvm_libc::strcpy(dest + 3, abc.c_str()); + ASSERT_EQ(dest + 3, result); + ASSERT_EQ(std::string(dest), std::string("xyz") + abc); + ASSERT_EQ(std::string(dest).size(), abc.size() + 3); + + delete[] dest; +} diff --git a/libc/utils/build_scripts/gen_hdr.py b/libc/utils/build_scripts/gen_hdr.py new file mode 100755 --- /dev/null +++ b/libc/utils/build_scripts/gen_hdr.py @@ -0,0 +1,188 @@ +#! /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 contextlib +import os +import sys + +COMMAND_PREFIX = "%%" +COMMENT_PREFIX = "" + +BEGIN_COMMAND = "begin" +COMMENT_COMMAND = "comment" +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) + + +@contextlib.contextmanager +def output_stream_manager(filename): + if filename is None: + try: + yield sys.stdout + finally: + pass + else: + output_stream = open(filename, "w") + try: + yield output_stream + finally: + output_stream.close() + + +def _parse_command(loc, line): + open_paren = line.find("(") + if open_paren < 0 or line[-1] != ")": + return _fatal_error(loc, "Incorrect header generation command syntax.") + command_name = line[len(COMMAND_PREFIX):open_paren] + args = line[open_paren + 1:-1].split(",") + args = [a.strip() for a in args] + if len(args) == 1 and not args[0]: + # There are no args, so we will make the args list an empty list. + args = [] + return command_name.strip(), args + + +def _is_named_arg(token): + if token.startswith("${") and token.endswith("}"): + return True + else: + return False + + +def _get_arg_name(token): + return token[2:-1] + + +def _fatal_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, values): + if len(args) != 1: + _fatal_error(loc, "`%%include_file` command takes exactly one " + "argument. %d given." % len(args)) + include_file_path = args[0] + if _is_named_arg(include_file_path): + arg_name = _get_arg_name(include_file_path) + include_file_path = values.get(arg_name) + if not include_file_path: + _fatal_error( + loc, + "No value specified for argument '%s'." % arg_name) + if not os.path.exists(include_file_path): + _fatal_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) + if args: + _fatal_error(loc, "Begin command does not take any args.") + begin = True + # Skip the line on which %%begin() is listed. + continue + if begin: + out_stream.write(line + "\n") + + +def begin_command(out_stream, loc, args, values): + # "begin" command can only occur in a file included with %%include_file + # command. It is not a replacement command. Hence, we just fail with + # a fatal error. + _fatal_error(loc, "Begin command cannot be listed in an input file.") + + +# Mapping from a command name to its implementation function. +REPLACEMENT_COMMANDS = { + INCLUDE_FILE_COMMAND: include_file_command, + BEGIN_COMMAND: begin_command, +} + + +def apply_replacement_command(out_stream, loc, line, values): + 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: + _fatal_error(loc, "Unknown replacement command `%`", command_name) + command(out_stream, loc, args, values) + + +def parse_options(): + 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("--args", "-P", nargs= "*", default=[], + help="NAME=VALUE pairs for command arguments 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 arg for arg in opts.args]): + # We want all args to be specified in the form "name=value". + _fatal_error( + __file__ + ":" + "[command line]", + "Command arguments should be listed in the form NAME=VALUE") + return opts + + +def main(): + opts = parse_options() + arg_values = {} + for name_value_pair in opts.args: + name, value = name_value_pair.split("=") + arg_values[name] = value + with open(opts.def_file, "r") as def_file: + loc = _Location(opts.def_file, 0) + with output_stream_manager(opts.out_file) as out_stream: + for line in def_file: + loc.line_number += 1 + line = line.strip() + if line.startswith(COMMAND_PREFIX): + replacement_text = apply_replacement_command( + out_stream, loc, line, arg_values) + out_stream.write("\n") + elif line.startswith(COMMENT_PREFIX): + # Ignore comment line + continue + else: + out_stream.write(line + "\n") + + +if __name__ == "__main__": + main() 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()