libmongocrypt
libmongocrypt copied to clipboard
MONGOCRYPT-535 Support libdfp as a Decimal128 abstraction
This changeset allows building using a system-installation of libdfp as an alternative to IntelDFP.
Change Summary
Package/CMake Changes
- Setting
MONGOCRYPT_DFP_DIRtoUSE-SYSTEMwill search for both IntelDFP and libdfp. If IntelDFP is found, it will be used; otherwise if libdfp is found, it will be used; otherwise we generate an error. - CMake code has been cleaned up to say "dfp" instead of "intel_dfp" throughout.
- The
_mongocrypt::dfpis anINTERFACEtarget alike what was previously defined, and is used to pivot the selection of a DFP backend. - The
_mongocrypt::system-dfpis anIMPORTEDtarget that points to the system-installed DFP library._mongocrypt::dfplinks to this library iffUSE-SYSTEMwas specified. - At import-time, the
_mongocrypt::system-dfplibrary needs to be redefined if we were built withUSE-SYSTEM, and a minimum amount offind_librarylogic was inserted intomongocrypt-config.cmaketo find the same library that was used for the build. This is similar to the prior version, but it now needs to consider whether we are built with IntelDFP or libdfp. We export the filename of the found library into themongocrypt-config.cmakeso that we can find the correct matching library later on. - The
.pcfor the shared library has the link to the DFP lib file removed since either: 1) We linked against a static DFP lib, and themongocrypt.soalready contains the required TUs, or 2) we built against a dynamic DFP lib, and themongocrypt.sowill already have the reference to the properSONAME. This makes the explicit linking unecessary in either case. The explicit link may fail if the generated.pccontains the path to a dev-only symlink (this is the case on e.g. Fedora Rawhide wherelibdfp.sois a symilnk tolibdfp.so.1, but thelibdfpruntime package only includeslibdfp.so.1). Alternative solution: Have libmongocrypt-dev depend on the libdfp-dev packages, but that feels like a heavier burden on users. - A few miscellaneous tweaks from uncovering some testing issues on some platforms (
add_testcan be finicky) - Test the RPM build using system's libdfp-devel instead of the bundled IntelDFP. Also tentatively: Use
libdfp-devinstead oflibintelrdfp-devas the build-dep for the libmongocrypt deb. This can be rolled back if we don't want to make that switch.
Code Changes
- We detect libdfp by
#includeingmath.h, and checking for_DFP_MATH_H. If found, it means that libdfp has intercepted the stdlib headers and the stdlib symbols may be replaced by the libdfp shims. This switchesmc-dec128.hto use libdfp as the backend instead of IntelDFP. - If libdfp is not detected, continue to use IntelDFP.
- Some
dec128classifying functions were renamed to use the stdlib-like names (e.g.is_inf→isinf). - The core of "implementation switching" is done with two function-like macros:
MC_IF_LIBDFP(...), which expands its arguments if libdfp is in use, andMC_IF_IntelDFP(...)which expands its arguments if libdfp is not in use. - Significant: libdfp uses the C stdlib
<fenv.h>facilities, which are awful and painful and Widely Regarded as a Bad Move. (Which is why the IntelDFP version opts for the explicit-env arguments for rounding and exceptions).- The fenv facilities are abstracted using
MC_HOLD_FENV(Rounding, FlagsPtr), which is a control-statement-like function-macro that wraps a code block and saves/restores the floating-point environment, while also setting the appropriate rounding mode and firingSIGFPEin the case of unhandled div-by-zero.
- The fenv facilities are abstracted using
- The to/from string functions were moved out-of-line into a separate TU, since
#include-order can affect the behavior of libdfp on the required functions, so we need to ensure it comes first in the TU. - The migration to libdfp uncovered some misbehaving test cases that were "just working" due to bad macro expansions. These cases have been fixed.
Alternative solution: Have libmongocrypt-dev depend on the libdfp-dev packages, but that feels like a heavier burden on users.
This is actually the correct solution. For instance, in the C driver, the libmongoc-dev package expresses the following dependencies:
Depends: libmongoc-1.0-0 (= ${binary:Version}),
libbson-dev (= ${binary:Version}),
libmongocrypt-dev,
libssl-dev,
zlib1g-dev,
libsnappy-dev,
libsasl2-dev,
libzstd-dev,
In general, we need that sort of thing to ensure access to a .so (the unversioned symlink), the .a (for libraries that support static linking), and also helper scripts (like .pc scripts for pkg-config and .cmake target files) to ensure appropriate compiler options/definitions are set and transitive link dependencies are resolved.