| # How to add a new math function to LLVM-libc | |
| This document is to serve as a cookbook for adding a new math function | |
| implementation to LLVM libc. To add a new function, apart from the actual | |
| implementation, one has to follow a few other steps to setup proper registration | |
| and shipping of the new function. Each of these steps will be described in | |
| detail below. | |
| ## Registration | |
| To register the function's entry points for supported OSes and architectures, | |
| together with its specifications: | |
| - Add entry points `libc.src.math.<func>` to the following files (only add on platforms where its supported): | |
| ``` | |
| libc/config/linux/<arch>/entrypoints.txt | |
| libc/config/baremetal/<arch>/entrypoints.txt | |
| libc/config/darwin/<arch>/entrypoints.txt | |
| libc/config/freebsd/<arch>/entrypoints.txt | |
| libc/config/gpu/<arch>/entrypoints.txt | |
| libc/config/windows/entrypoints.txt | |
| ``` | |
| - Add function specs to the file: | |
| ``` | |
| libc/include/math.yaml | |
| ``` | |
| ## Implementation | |
| The function's actual implementation is defined in an internal header, while the public entry point is a thin wrapper | |
| added to the following locations: | |
| - Add the core math logic (under `LIBC_NAMESPACE::math` namespace) to: | |
| ``` | |
| libc/src/__support/math/<func>.h | |
| ``` | |
| - Add the corresponding `add_header_library` to: | |
| ``` | |
| libc/src/__support/math/CMakeLists.txt | |
| ``` | |
| - Add `add_math_entrypoint_object(<func>)` to: | |
| ``` | |
| libc/src/math/CMakeLists.txt | |
| ``` | |
| - Add function declaration (under `LIBC_NAMESPACE` namespace) to: | |
| ``` | |
| libc/src/math/<func>.h | |
| ``` | |
| - Add function definition (calling the __support implementation) to: | |
| ``` | |
| libc/src/math/generic/<func>.cpp | |
| ``` | |
| - Add the corresponding `add_entrypoint_object` to: | |
| ``` | |
| libc/src/math/generic/CMakeLists.txt | |
| ``` | |
| - Add architectural specific implementations to: | |
| ``` | |
| libc/src/math/<arch>/<func>.cpp | |
| ``` | |
| ### Shared Math Library | |
| If the function should be available to internal LLVM projects: | |
| - Include the support header in: | |
| ``` | |
| libc/shared/math.h | |
| ``` | |
| - Add a header that exports the function via using in: | |
| ``` | |
| libc/shared/math/<func>.h | |
| ``` | |
| - Add a simple test case to: | |
| ``` | |
| libc/test/shared/shared_math_test.cpp | |
| ``` | |
| - Add the corresponding entry `libc.src.__support.math.<func>` to: | |
| ``` | |
| libc/test/shared/CMakeLists.txt | |
| ``` | |
| - Add the corresponding `libc_support_library` and `libc_math_function` to: | |
| ``` | |
| utils/bazel/llvm-project-overlay/libc/BUILD.bazel | |
| ``` | |
| ### Floating point utility | |
| - Floating point utilities and math functions that are also used internally are | |
| located at: | |
| ``` | |
| libc/src/__support/FPUtil | |
| ``` | |
| - These are preferred to be included as header-only. | |
| - To manipulate bits of floating point numbers, use the template class | |
| `LIBC_NAMESPACE::fputil::FPBits<>` in the header file: | |
| ``` | |
| libc/src/__support/FPUtil/FPBits.h | |
| ``` | |
| ## Testing | |
| ### MPFR utility | |
| In addition to the normal testing macros such as `EXPECT_EQ, ASSERT_THAT, ...` | |
| there are two special macros `ASSERT_MPFR_MATCH` and `EXPECT_MPFR_MATCH` to | |
| compare your outputs with the corresponding MPFR function. In | |
| order for your new function to be supported by these two macros, | |
| the following files will need to be updated: | |
| - Add the function enum to `LIBC_NAMESPACE::testing::mpfr::Operation` in the | |
| header file: | |
| ``` | |
| libc/utils/MPFRWrapper/MPFRUtils.h | |
| ``` | |
| - Add support for `func` in the `MPFRNumber` class and the corresponding link | |
| between the enum and its call to the file: | |
| ``` | |
| libc/utils/MPFRWrapper/MPFRUtils.cpp | |
| ``` | |
| ### Unit tests | |
| Besides the usual testing macros like `EXPECT_EQ, ASSERT_TRUE, ...` there are | |
| testing macros specifically used for floating point values, such as | |
| `EXPECT_FP_EQ, ASSERT_FP_LE, ...` | |
| - Add smoke tests (simple cases and zeros / inf / nan inputs or outputs) to: | |
| ``` | |
| libc/test/src/math/smoke/<func>_test.cpp | |
| ``` | |
| - Add unit test that might require MPFR to: | |
| ``` | |
| libc/test/src/math/<func>_test.cpp | |
| ``` | |
| - Add the corresponding entry points to: | |
| ``` | |
| libc/test/src/math/smoke/CMakeLists.txt | |
| libc/test/src/math/CMakeLists.txt | |
| ``` | |
| ### Exhaustive tests | |
| Exhaustive tests are long-running tests that are not included when you run | |
| `ninja check-libc`. These exhaustive tests are added and manually run in | |
| order to find exceptional cases for your function's implementation. | |
| - Add an exhaustive test to: | |
| ``` | |
| libc/test/src/math/exhaustive/<func>_test.cpp | |
| ``` | |
| - Add the corresponding entry point to: | |
| ``` | |
| libc/test/src/math/exhaustive/CMakeLists.txt | |
| ``` | |
| - The template class `LlvmLibcExhaustiveMathTest` located at: | |
| ``` | |
| libc/test/src/math/exhaustive/exhaustive_test.h | |
| ``` | |
| can be used for conveniently parallelizing the exhaustive tests. | |
| ### Performance tests | |
| Performance tests compare your function's implementation with the system libc | |
| implementation (which is very often glibc). | |
| - Add a performance test to: | |
| ``` | |
| libc/test/src/math/performance_testing/<func>_perf.cpp | |
| ``` | |
| - Add the corresponding entry point to: | |
| ``` | |
| libc/test/src/math/performance_testing/CMakeLists.txt | |
| ``` | |
| ## Build and Run | |
| - Check out the LLVM source tree: | |
| ``` | |
| $ git clone https://github.com/llvm/llvm-project.git | |
| ``` | |
| - Compiler Requirements: The libc implementation should support compiling with both Clang and GCC (12.2). | |
| - Setup projects with CMake: | |
| ``` | |
| $ cd llvm-project | |
| $ mkdir build | |
| $ cd build | |
| $ cmake ../llvm -G Ninja \ | |
| -DLLVM_ENABLE_RUNTIMES="libc" \ | |
| -DCMAKE_BUILD_TYPE=Debug \ | |
| -DCMAKE_C_COMPILER=clang \ | |
| -DCMAKE_CXX_COMPILER=clang++ | |
| $ cd runtimes/runtimes-bins/ | |
| ``` | |
| - Build the whole `libc`: | |
| ``` | |
| $ ninja libc | |
| ``` | |
| - Run all unit tests: | |
| ``` | |
| $ ninja check-libc | |
| ``` | |
| - Run math smoke tests only: | |
| ``` | |
| $ ninja libc-math-smoke-tests | |
| ``` | |
| - Run math smoke and unit tests: | |
| ``` | |
| $ ninja libc-math-unittests | |
| ``` | |
| - Build and Run a specific unit test: | |
| ``` | |
| $ ninja libc.test.src.math.<func>_test.__unit__ | |
| $ projects/libc/test/src/math/libc.test.src.math.<func>_test.__unit__ | |
| ``` | |
| - Build and Run shared math test: | |
| ``` | |
| $ ninja libc.test.shared.shared_math_test.__unit__ | |
| $ projects/libc/test/shared/libc.test.shared.shared_math_test.__unit__ | |
| ``` | |
| - Build and Run exhaustive test (might take hours to run): | |
| ``` | |
| $ ninja libc.test.src.math.exhaustive.<func>_test.__unit__ | |
| $ projects/libc/test/src/math/exhaustive/libc.test.src.math.exhaustive.<func>_test.__unit__ | |
| ``` | |
| - Build and Run performance test: | |
| ``` | |
| $ ninja libc.test.src.math.performance_testing.<func>_perf | |
| $ projects/libc/test/src/math/performance_testing/libc.test.src.math.performance_testing.<func>_perf | |
| $ cat <func>_perf.log | |
| ``` | |
| ## Code reviews | |
| We use GitHub's inbuilt pull request system for code review: | |
| ``` | |
| https://docs.github.com/articles/about-collaborative-development-models | |
| https://docs.github.com/articles/about-pull-requests | |
| ``` |