Add few docs and implementation of strcpy and strcat.
authorSiva Chandra <sivachandra@google.com>
Fri, 4 Oct 2019 17:30:54 +0000 (17:30 +0000)
committerSiva Chandra <sivachandra@google.com>
Fri, 4 Oct 2019 17:30:54 +0000 (17:30 +0000)
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

29 files changed:
libc/CMakeLists.txt [new file with mode: 0644]
libc/cmake/modules/LLVMLibCRules.cmake [new file with mode: 0644]
libc/docs/build_system.rst [new file with mode: 0644]
libc/docs/entrypoints.rst [new file with mode: 0644]
libc/docs/header_generation.rst [new file with mode: 0644]
libc/docs/implementation_standard.rst [new file with mode: 0644]
libc/docs/source_layout.rst [new file with mode: 0644]
libc/include/CMakeLists.txt [new file with mode: 0644]
libc/include/__llvm-libc-common.h [new file with mode: 0644]
libc/include/ctype.h [new file with mode: 0644]
libc/include/math.h [new file with mode: 0644]
libc/include/string.h [new file with mode: 0644]
libc/lib/CMakeLists.txt [new file with mode: 0644]
libc/src/CMakeLists.txt [new file with mode: 0644]
libc/src/__support/CMakeLists.txt [new file with mode: 0644]
libc/src/__support/common.h.def [new file with mode: 0644]
libc/src/__support/linux/entrypoint_macro.h.inc [new file with mode: 0644]
libc/src/string/CMakeLists.txt [new file with mode: 0644]
libc/src/string/strcat/CMakeLists.txt [new file with mode: 0644]
libc/src/string/strcat/strcat.cpp [new file with mode: 0644]
libc/src/string/strcat/strcat.h [new file with mode: 0644]
libc/src/string/strcat/strcat_test.cpp [new file with mode: 0644]
libc/src/string/strcpy/CMakeLists.txt [new file with mode: 0644]
libc/src/string/strcpy/strcpy.cpp [new file with mode: 0644]
libc/src/string/strcpy/strcpy.h [new file with mode: 0644]
libc/src/string/strcpy/strcpy_test.cpp [new file with mode: 0644]
libc/utils/build_scripts/gen_hdr.py [new file with mode: 0755]
llvm/CMakeLists.txt
llvm/projects/CMakeLists.txt

diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
new file mode 100644 (file)
index 0000000..025d2bb
--- /dev/null
@@ -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 (file)
index 0000000..ea43e51
--- /dev/null
@@ -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)
diff --git a/libc/docs/build_system.rst b/libc/docs/build_system.rst
new file mode 100644 (file)
index 0000000..f34ffe0
--- /dev/null
@@ -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``.
diff --git a/libc/docs/entrypoints.rst b/libc/docs/entrypoints.rst
new file mode 100644 (file)
index 0000000..dfc0aec
--- /dev/null
@@ -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.
diff --git a/libc/docs/header_generation.rst b/libc/docs/header_generation.rst
new file mode 100644 (file)
index 0000000..99b1177
--- /dev/null
@@ -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.
diff --git a/libc/docs/implementation_standard.rst b/libc/docs/implementation_standard.rst
new file mode 100644 (file)
index 0000000..bba4fe6
--- /dev/null
@@ -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.
diff --git a/libc/docs/source_layout.rst b/libc/docs/source_layout.rst
new file mode 100644 (file)
index 0000000..b06ad96
--- /dev/null
@@ -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.
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2859602
--- /dev/null
@@ -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
+)
diff --git a/libc/include/__llvm-libc-common.h b/libc/include/__llvm-libc-common.h
new file mode 100644 (file)
index 0000000..7dc2f9e
--- /dev/null
@@ -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
diff --git a/libc/include/ctype.h b/libc/include/ctype.h
new file mode 100644 (file)
index 0000000..27bb864
--- /dev/null
@@ -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
diff --git a/libc/include/math.h b/libc/include/math.h
new file mode 100644 (file)
index 0000000..6cd258b
--- /dev/null
@@ -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
diff --git a/libc/include/string.h b/libc/include/string.h
new file mode 100644 (file)
index 0000000..020c543
--- /dev/null
@@ -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
diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt
new file mode 100644 (file)
index 0000000..49e2d2f
--- /dev/null
@@ -0,0 +1,9 @@
+
+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 (file)
index 0000000..b661a65
--- /dev/null
@@ -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 (file)
index 0000000..a536e90
--- /dev/null
@@ -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 (file)
index 0000000..b2605a7
--- /dev/null
@@ -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 (file)
index 0000000..8f873f9
--- /dev/null
@@ -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 (file)
index 0000000..a8984f6
--- /dev/null
@@ -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 (file)
index 0000000..790d77b
--- /dev/null
@@ -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.cpp b/libc/src/string/strcat/strcat.cpp
new file mode 100644 (file)
index 0000000..09cc62d
--- /dev/null
@@ -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.h b/libc/src/string/strcat/strcat.h
new file mode 100644 (file)
index 0000000..d3023e9
--- /dev/null
@@ -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
diff --git a/libc/src/string/strcat/strcat_test.cpp b/libc/src/string/strcat/strcat_test.cpp
new file mode 100644 (file)
index 0000000..26bcae2
--- /dev/null
@@ -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;
+}
diff --git a/libc/src/string/strcpy/CMakeLists.txt b/libc/src/string/strcpy/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9f27911
--- /dev/null
@@ -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.cpp b/libc/src/string/strcpy/strcpy.cpp
new file mode 100644 (file)
index 0000000..0dfb1e3
--- /dev/null
@@ -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
diff --git a/libc/src/string/strcpy/strcpy.h b/libc/src/string/strcpy/strcpy.h
new file mode 100644 (file)
index 0000000..67710d8
--- /dev/null
@@ -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
diff --git a/libc/src/string/strcpy/strcpy_test.cpp b/libc/src/string/strcpy/strcpy_test.cpp
new file mode 100644 (file)
index 0000000..48f55f2
--- /dev/null
@@ -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;
+}
diff --git a/libc/utils/build_scripts/gen_hdr.py b/libc/utils/build_scripts/gen_hdr.py
new file mode 100755 (executable)
index 0000000..54ee006
--- /dev/null
@@ -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 4ebc726..7c49f13 100644 (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" )
index 47ceb9e..7a948bd 100644 (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()