Index: libc/trunk/CMakeLists.txt =================================================================== --- libc/trunk/CMakeLists.txt +++ libc/trunk/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) Index: libc/trunk/cmake/modules/LLVMLibCRules.cmake =================================================================== --- libc/trunk/cmake/modules/LLVMLibCRules.cmake +++ libc/trunk/cmake/modules/LLVMLibCRules.cmake @@ -0,0 +1,280 @@ + +# 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 + "DEPENDS" # 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} + ) + + if(ADD_HEADER_DEPENDS) + add_dependencies( + ${target_name} ${ADD_HEADER_DEPENDS} + ) + endif() +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,weak,global" ${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} + "OBJECT_FILE_RAW" ${object_file_raw} + ) +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_RAW") + 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) Index: libc/trunk/docs/build_system.rst =================================================================== --- libc/trunk/docs/build_system.rst +++ libc/trunk/docs/build_system.rst @@ -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 entrypoint 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``. Index: libc/trunk/docs/entrypoints.rst =================================================================== --- libc/trunk/docs/entrypoints.rst +++ libc/trunk/docs/entrypoints.rst @@ -0,0 +1,6 @@ +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 in LLVM-libc's +source layout, build system and source code. Index: libc/trunk/docs/header_generation.rst =================================================================== --- libc/trunk/docs/header_generation.rst +++ libc/trunk/docs/header_generation.rst @@ -0,0 +1,98 @@ +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. Index: libc/trunk/docs/implementation_standard.rst =================================================================== --- libc/trunk/docs/implementation_standard.rst +++ libc/trunk/docs/implementation_standard.rst @@ -0,0 +1,85 @@ +Convention for implementing entrypoints +======================================= + +LLVM-libc entrypoints are defined in the entrypoints document. In this document, +we explain how the entrypoints are implemented. The source layout 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 + +NOTE: We use a post build ``objcopy`` step to add an alias instead of using +the ``__attribute__((alias))``. For C++, this ``alias`` attribute requires +mangled names of the referees. Using the post build ``objcopy`` step helps +us avoid putting mangled names with ``alias`` atttributes. Index: libc/trunk/docs/source_layout.rst =================================================================== --- libc/trunk/docs/source_layout.rst +++ libc/trunk/docs/source_layout.rst @@ -0,0 +1,85 @@ +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 + 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. 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``. The + implementation standard document explains more about the *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. Index: libc/trunk/include/CMakeLists.txt =================================================================== --- libc/trunk/include/CMakeLists.txt +++ libc/trunk/include/CMakeLists.txt @@ -0,0 +1,30 @@ + +add_header( + llvm_libc_common_h + HDR + __llvm-libc-common.h +) + +add_header( + ctype_h + HDR + ctype.h + DEPENDS + llvm_libc_common_h +) + +add_header( + math_h + HDR + math.h + DEPENDS + llvm_libc_common_h +) + +add_header( + string_h + HDR + string.h + DEPENDS + llvm_libc_common_h +) Index: libc/trunk/include/__llvm-libc-common.h =================================================================== --- libc/trunk/include/__llvm-libc-common.h +++ libc/trunk/include/__llvm-libc-common.h @@ -0,0 +1,33 @@ +//===------- Common definitions for LLVM-libc public 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC___COMMON_H +#define LLVM_LIBC___COMMON_H + +#ifdef __cplusplus + +#undef __BEGIN_C_DECLS +#define __BEGIN_C_DECLS extern "C" { + +#undef __END_C_DECLS +#define __END_C_DECLS } + +#else // not __cplusplus + +#undef __BEGIN_C_DECLS +#define __BEGIN_C_DECLS + +#undef __END_C_DECLS +#define __END_C_DECLS + +#undef __restrict +#define __restrict restrict // C99 and above support the restrict keyword. + +#endif // __cplusplus + +#endif // LLVM_LIBC___COMMON_H Index: libc/trunk/include/ctype.h =================================================================== --- libc/trunk/include/ctype.h +++ libc/trunk/include/ctype.h @@ -0,0 +1,46 @@ +//===---------------- 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 + +#include <__llvm-libc-common.h> + +__BEGIN_C_DECLS + +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); + +__END_C_DECLS + +#endif // LLVM_LIBC_CTYPE_H Index: libc/trunk/include/math.h =================================================================== --- libc/trunk/include/math.h +++ libc/trunk/include/math.h @@ -0,0 +1,360 @@ +//===----------------- 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 + +#include <__llvm-libc-common.h> + +__BEGIN_C_DECLS + +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); + +__END_C_DECLS + +#endif // LLVM_LIBC_MATH_H Index: libc/trunk/include/string.h =================================================================== --- libc/trunk/include/string.h +++ libc/trunk/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 <__llvm-libc-common.h> + +#define __need_size_t // To get only size_t from stddef.h +#define __need_NULL // To get only NULL from stddef.h +#include + +__BEGIN_C_DECLS + +void *memcpy(void *__restrict, const void *__restrict, 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 *__restrict, const char *__restrict); + +char *strncpy(char *__restrict, const char *__restrict, size_t); + +char *strcat(char *__restrict, const char *__restrict); + +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 *__restrict, const char *__restrict, 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 *__restrict, const char *__restrict); + +char *strerror(int); + +size_t strlen(const char *); + +__END_C_DECLS + +#endif // LLVM_LIBC_STRING_H Index: libc/trunk/lib/CMakeLists.txt =================================================================== --- libc/trunk/lib/CMakeLists.txt +++ libc/trunk/lib/CMakeLists.txt @@ -0,0 +1,9 @@ + +add_entrypoint_library( + llvmlibc + DEPENDS + ### C standard library entrypoints + # string.h entrypoints + strcpy + strcat +) Index: libc/trunk/src/CMakeLists.txt =================================================================== --- libc/trunk/src/CMakeLists.txt +++ libc/trunk/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(string) + +add_subdirectory(__support) Index: libc/trunk/src/__support/CMakeLists.txt =================================================================== --- libc/trunk/src/__support/CMakeLists.txt +++ libc/trunk/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 +) Index: libc/trunk/src/__support/common.h.def =================================================================== --- libc/trunk/src/__support/common.h.def +++ libc/trunk/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 Index: libc/trunk/src/__support/linux/entrypoint_macro.h.inc =================================================================== --- libc/trunk/src/__support/linux/entrypoint_macro.h.inc +++ libc/trunk/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 Index: libc/trunk/src/string/CMakeLists.txt =================================================================== --- libc/trunk/src/string/CMakeLists.txt +++ libc/trunk/src/string/CMakeLists.txt @@ -0,0 +1,4 @@ +add_custom_target(libc_string_unittests) + +add_subdirectory(strcpy) +add_subdirectory(strcat) Index: libc/trunk/src/string/strcat/CMakeLists.txt =================================================================== --- libc/trunk/src/string/strcat/CMakeLists.txt +++ libc/trunk/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 +) Index: libc/trunk/src/string/strcat/strcat.h =================================================================== --- libc/trunk/src/string/strcat/strcat.h +++ libc/trunk/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 Index: libc/trunk/src/string/strcat/strcat.cpp =================================================================== --- libc/trunk/src/string/strcat/strcat.cpp +++ libc/trunk/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 Index: libc/trunk/src/string/strcat/strcat_test.cpp =================================================================== --- libc/trunk/src/string/strcat/strcat_test.cpp +++ libc/trunk/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; +} Index: libc/trunk/src/string/strcpy/CMakeLists.txt =================================================================== --- libc/trunk/src/string/strcpy/CMakeLists.txt +++ libc/trunk/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 +) Index: libc/trunk/src/string/strcpy/strcpy.h =================================================================== --- libc/trunk/src/string/strcpy/strcpy.h +++ libc/trunk/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 Index: libc/trunk/src/string/strcpy/strcpy.cpp =================================================================== --- libc/trunk/src/string/strcpy/strcpy.cpp +++ libc/trunk/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 Index: libc/trunk/src/string/strcpy/strcpy_test.cpp =================================================================== --- libc/trunk/src/string/strcpy/strcpy_test.cpp +++ libc/trunk/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; +} Index: libc/trunk/utils/build_scripts/gen_hdr.py =================================================================== --- libc/trunk/utils/build_scripts/gen_hdr.py +++ libc/trunk/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() Index: llvm/trunk/CMakeLists.txt =================================================================== --- llvm/trunk/CMakeLists.txt +++ llvm/trunk/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" ) Index: llvm/trunk/projects/CMakeLists.txt =================================================================== --- llvm/trunk/projects/CMakeLists.txt +++ llvm/trunk/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()