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 entrypoints live in the directory for the header they belong to. Some entrypoints are platform specific, and so their implementations are in a subdirectory with the name of the platform (e.g. stdio/linux/remove.cpp).

Implementation of entrypoints can span multiple .cpp and .h files, but there will be at least one header file with name of the form <entrypoint name>.h for every entrypoint. This header file is called the implementation header file. For the isalpha function, the path to the implementation header file is src/ctype/isalpha.h.

Implementation Header File Structure

We will use the isalpha function from the public ctype.h header file as an example. The isalpha function will be declared in an internal header file src/ctype/isalpha.h as follows:

// --- isalpha.h --- //

namespace LIBC_NAMESPACE {

int isalpha(int c);

} // namespace LIBC_NAMESPACE


Notice that the isalpha function declaration is nested inside the namespace LIBC_NAMESPACE. All implementation constructs in LLVM-libc are declared within the namespace LIBC_NAMESPACE.

.cpp File Structure

The main .cpp file is named <entrypoint name>.cpp and is usually in the same folder as the header. It contains the signature of the entrypoint function, which must be defined with the LLVM_LIBC_FUNCTION macro. For example, the isalpha function from ctype.h is defined as follows, in the file src/ctype/isalpha.cpp:

// --- isalpha.cpp --- //

namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(int, isalpha, (int c)) {
  // ... implementation goes here.

} // namespace LIBC_NAMESPACE

Notice the use of the macro LLVM_LIBC_FUNCTION. This macro helps us define a C alias symbol for the C++ implementation. For example, for a library build, the macro is defined as follows:

#define LLVM_LIBC_FUNCTION(type, name, arglist)
    LLVM_LIBC_FUNCTION_IMPL(type, name, arglist)
#define LLVM_LIBC_FUNCTION_IMPL(type, name, arglist)
        __##name##_impl__ __asm__(#name);
    decltype(LIBC_NAMESPACE::name) name [[gnu::alias(#name)]];
    type __##name##_impl__ arglist

The LLVM_LIBC_FUNCTION_ATTR macro is normally defined to nothing, but can be defined by vendors who want to set their own attributes.