Just another blogs.freebsdish.org site

In the last few months, we made some changes to math.h (and in C++’s <cmath>) to clean up the isinf(), isnan() and related functions. These are defined by the C and C++ spec for all scalar floating point types. Our previous code used a slightly hacky implementation, which appears to be shared with most other libc implementations.

Since these macros are defined for float, double and long double, we were disambiguating the versions by comparing the size of the argument and the size of various types, with code something like this:

This works fine as long as the macros are only called with floating point types. But what happens if they are invoked with an int or perhaps a vector of two ints? The good news for our implementation is that this is undefined behaviour, so we are allowed to do whatever we want. This code called with, for example, isnan(1) would first cast the int value of 1 to a float (because sizeof(int) is the same as sizeof(float). On an ILP64 platform, it would cast it to a double) and then called the function that made sense for floats.

This is a valid thing to do, but it’s also nonsense. It hides lots of potential logic errors. If you’re calling isnan(int) or isinf(int) then you’re almost certainly doing something wrong: ints are always numbers and they are always finite.

The new version avoids this. It still falls back to the old code that works for any standards-compliant compiler, but if your compiler supports C11 _Generic() expressions (clang in the base system does), or the __builtin_types_compatible_p() and __builtin_choose_expr() GNU extensions then it uses them instead. The former is very simple: it provides a way of selecting the expression to evaluate based on its type. The above macro becomes something like this:

Not only is this more readable, it also generates a compile-time error if you try to invoke it with a type that is not listed. You can also provide a default: case if you want to emulate the old behaviour. The version using the GNU extensions is semantically equivalent, but a lot less readable. The __builtin_choose_expr() builtin lets you select between two cases based on a compile-time constant expression. The __builtin_types_compatible_p() builtin lets you compare whether two types are the same (so typeof(x) is compared to float, double, and long double, in turn).

A fairly simple change, which didn’t make any difference for any software that was not relying on undefined behaviour. And yet it generated a surprising amount of fallout in the ports tree. One common problem was that a lot of configure scripts checked for the existence of the isnan() macro by trying to do isnan(1) in their configure scripts. Examples of this included the V8 JavaScript engine and the Mono compiler.

Mono was especially bad, because its configure script checked for the existence of isnan(int) and, if it didn’t find it, then it defined its own isnan(double) to use instead. I’m slightly alarmed at the idea of using a compiler written by people who don’t know the difference between int and double.

These were easy to fix. You can easily check for these functions by doing isnan(1.0) or isnan(1.0f) (if you want to check for float support as well as or instead of double), so the patches to the configure scripts were quite small.

More surprising was that a number of programs, for example Blender, actually call things like isnan(unsigned int) in their code. These are real bugs and they’re found because we turned on slightly stricter compile-time checking in libc. Are there any C standard library APIs that you’ve seen frequently used wrongly and would like stronger checking for?

This week, I imported a new device tree compiler, dtc(1). This is the tool that is used to translate between different representations of a Flattened Device Tree (FDT), a way of representing boot-time configuration information. The FDT contains more or less the same information as an OpenFirmware device tree, for example the locations of memory-mapped peripherals, reserved sections of RAM, interrupt routing information, and so on.

FreeBSD/ARM makes a lot of use of FDTs, as they’re the standard way of getting information from the bootloader. They are used in two ways. The ideal way is for the bootloader to provide the tree to the kernel at boot time. This allows a single kernel to be used with multiple SoCs. Alternatively, the device tree can be compiled into the kernel.

The device tree in both cases is in the form of a Device Tree Blob (DTB), a binary representation of the tree. The ‘flattened’ in the name comes from the fact that the tree is represented in a linear structured format, like HTML, with explicit delimiters for starts and ends of child nodes, rather than in a format with pointers between elements. The other representation, the Device Tree Source is a human-readable tree using braces to delimit children and is rather similar to the OpenStep property list format or JSON.

The device tree compiler is responsible for converting between these formats. In the FreeBSD tree, we have a number of DTS files that represent supported platforms where the bootloader doesn’t provide the DTB. During the build process, the DTB is generated and linked into the kernel.

Unfortunately, the existing tool was released under the GPL. We try to minimise the amount of GPL’d code installed by default, and intend to remove all of it by 10.0, so dtc was not installed unless your target platform used it (a bit silly, as you want to use it on your host platform when doing embedded device development). The new tool is a (BSD-licensed) from-scratch rewrite that I did over Christmas. It shares no code with the original, but works as a drop-in replacement in our build system.

It is now used by default, although the old GPL’d tool will remain available as an option for a while until I’m confident that we aren’t breaking out-of-tree dtc users. So, if you’re using FDTs and don’t have the DTS in the tree, please test it. Otherwise, this is just another step on the way to a fully GPL-free base system.

If you read the release announcement, then you’ll have seen that there is a new C++ stack in 9.1, but that it is marked optional and is not part of the default binary install. If you’re a C++ developer, you may want to play with it for a few reasons:

It supports C++11 (which is new, shiny, and buzzwordy)

It will be the only one shipped by default in 10.0, so if you care about forward compatibility then you will need to test with it or make sure that your code works with it, or depend on GNU libstdc++ from ports.

The new stack uses libcxxrt, which I originally wrote for PathScale and was since open sourced (funded by the FreeBSD and NetBSD Foundations). Â This implements the low-level part of the C++ standard library, providing things like support for RTTI, dynamic_cast, exceptions, thread-safe initialisation of statics, and so on. In the old stack, libsupc++ does this.

On top of this sits the STL implementation. In the old stack, this was GNU libstdc++, in the new one it’s LLVM’s libc++. To make interoperability easier, in 9.1 we have made libstdc++ dynamically link against libsupc++. You can use libmap.conf(5) to tell it to link against libcxxrt instead, and then you can mix libraries that use libc++ with ones that use libstdc++ in the same program.

The new stack isn’t installed by default, but building and installing it is very easy:

You should now have libc++.so installed in /usr/lib and the headers installed in /usr/include/c++/v1. You are now ready to compile C++ code with the new stack. You use them, clang has a command-line switch for selecting the stack to use. By default, it will still use the old stack:

Unfortunately, in 9.1 there is a small bug that prevents you from compiling in C++98 mode with libc++. The C11 quick_exit() and at_quick_exit() functions are exposed in our stdlib.h only in C11 and C++11 modes, but are referenced in libc++’s cstdlib in any mode.

Most code should work out-of-the-box in C++11 mode though, so there’s little reason not to try it. And, because of the availability of move semantics, some code using STL classes will be more efficient when compiled in C++11 mode.