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.
Implementation 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
attributes.