Add few docs and implementation of strcpy and strcat.

Summary:
This patch illustrates some of the features like modularity we want
in the new libc. Few other ideas like different kinds of testing, redirectors
etc are not yet present.

Reviewers: dlj, hfinkel, theraven, jfb, alexshap, jdoerfert

Subscribers: mgorny, dexonsmith, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D67867

llvm-svn: 373764
This commit is contained in:
Siva Chandra 2019-10-04 17:30:54 +00:00
parent 717e540f7e
commit 4380647e79
29 changed files with 1588 additions and 1 deletions

24
libc/CMakeLists.txt Normal file
View File

@ -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)

View File

@ -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(
# <target name>
# HDR <header file>
# )
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(
# <target name>
# DEF_FILE <.h.def file>
# GEN_HDR <generated header file name>
# PARAMS <list of name=value pairs>
# DATA_FILES <list input 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(
# <target_name>
# SRCS <list of .cpp files>
# HDRS <list of .h files>
# DEPENDS <list of dependencies>
# )
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 $<TARGET_OBJECTS:${target_name}_objects>
COMMAND ${CMAKE_LINKER} -r $<TARGET_OBJECTS:${target_name}_objects> -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 <list of add_entrypoint_object targets>
# )
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 $<TARGET_FILE:${target_name}>
)
if(LIBC_UNITTEST_SUITE)
add_dependencies(
${LIBC_UNITTEST_SUITE}
${target_name}
)
endif()
endfunction(add_libc_unittest)

View File

@ -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``.

View File

@ -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.

View File

@ -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.

View File

@ -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
``<entrypoint name>.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.<function name>``. 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.

View File

@ -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.

View File

@ -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
)

View File

@ -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

46
libc/include/ctype.h Normal file
View File

@ -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

360
libc/include/math.h Normal file
View File

@ -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

66
libc/include/string.h Normal file
View File

@ -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 <stddef.h>
__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

9
libc/lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
add_entrypoint_library(
llvmlibc
DEPENDS
### C standard library entrypoints
# string.h entrypoints
strcpy
strcat
)

3
libc/src/CMakeLists.txt Normal file
View File

@ -0,0 +1,3 @@
add_subdirectory(string)
add_subdirectory(__support)

View File

@ -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
)

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
add_custom_target(libc_string_unittests)
add_subdirectory(strcpy)
add_subdirectory(strcat)

View File

@ -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
)

View File

@ -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

View File

@ -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 <string.h>
namespace __llvm_libc {
char *strcat(char *dest, const char *src);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_STRING_STRCAT_H

View File

@ -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 <string>
#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;
}

View File

@ -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
)

View File

@ -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<char *>(::memcpy(dest, src, ::strlen(src) + 1));
}
} // namespace __llvm_libc

View File

@ -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 <string.h>
namespace __llvm_libc {
char *strcpy(char *dest, const char *src);
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_STRING_STRCPY_H

View File

@ -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 <string>
#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;
}

View File

@ -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()

View File

@ -59,7 +59,7 @@ endif()
# 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" )

View File

@ -31,6 +31,7 @@ if(${LLVM_BUILD_RUNTIME})
# 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()