5.6 KiB
Built-Ins
This module provides portable implementations of many compiler builtins and intrinsics, allowing you to use builtins and intrinsics on compilers which don't support them. This includes other compilers (e.g., GCC builtins on MSVC) and older versions of the same compiler.
We also provide exact-width variants of many builtins; no more calling
different functions depending on the size of int, long, long long, etc. These are typically just aliases for the appropriate
function, but if we can't find an appropriate type a portable
implementation will be used.
If you define PSNIP_BUILTIN_EMULATE_NATIVE before builtin.h is
included, this header will also define any missing native-style
built-ins, allowing you to use the native names without regard for
which compiler is actually in use (i.e., you can use __builtin_ffs
directly in MSVC, or any other compiler, including GCC < 3.3).
If the compiler already has the builtin, the psnip function will
simply be defined to that builtin (e.g.,
#define psnip_builtin_clz __builtin_clz). If the compiler does not
have an implementation one will be provided using either a
built-in/intrinsic the compiler does support (e.g., using an MSVC
intrinsic to implement a GCC built-in), inline assembly,
architecture-specific functions, or a fully-portable pure C
implementation.
For example, for GCC's __builtin_ffs builtin, we provide
implementations which work everywhere (including versions of GCC prior
to 3.3, when __builtin_ffs was introduced) of the following
functions:
int psnip_builtin_ffs(int);
int psnip_builtin_ffsl(long);
int psnip_builtin_ffsll(long long);
int psnip_builtin_ffs32(psnip_int32_t);
int psnip_builtin_ffs64(psnip_int64_t);
Additionally, when when PSNIP_BUILTIN_EMULATE_NATIVE is defined (and the compiler doesn't already provide them), we also provide
int __builtin_ffs(int);
int __builtin_ffsl(long);
int __builtin_ffsll(long long);
Note that these are often provided as macros, the prototypes are for documentation only.
Dependencies
To maximize portability you should #include the exact-int module before including builtin.h, but if you don't want to add the extra file to your project you can omit it and this module will simply rely on <stdint.h>. As an alternative you may define the following macros to appropriate values yourself:
psnip_int8_tpsnip_uint8_tpsnip_int16_tpsnip_uint16_tpsnip_int32_tpsnip_uint32_tpsnip_int64_tpsnip_uint64_t
Implementation Status
Virtually every generic builtin we can implement has been implemented. This should work almost everywhere, but every commit is tested before landing in the master branch on various versions of GCC, clang, MSVC, and PGI (thanks to Travis CI and AppVeyor). Sporadic testing is also done on ICC and Oracle Developer Studio.
GCC builtins:
- ffs, ffsl, ffsll, ffs32, ffs64
- clz, clzl, clzll, clz32, clz64
- ctz, ctzl, ctzll, ctz32, ctz64
- clrsb, clrsbl, clrsbll, clrsb32, clrsb64
- popcount, popcountl, popcountll, popcount32, popcount64
- parity, parityl, parityll, parity32, parity64
- bswap16, bswap32, bswap64
Clang builtins:
- bitreverse8, bitreverse16, bitreverse32, bitreverse64
- addcb, addcs, addc, addcl, addcll, addc8, addc16, addc32, addc64
- subcb, subcs, subc, subcl, subcll, subc8, subc16, subc32, subc64
MSVC intrinsics:
- rotl8, rotl16, rotl, rotl64
- rotr8, rotr16, rotr, rotr64
- BitScanForward, BitScanForward64
- BitScanReverse, BitScanReverse64
- mul128, umul128
- shiftleft128, shiftright128
- mulh, umulh
- byteswap_ushort, byteswap_ulong, byteswap_uint64
- bittest, bittest64
- bittestandcomplement, bittestandcomplement64
- bittestandreset, bittestandreset64
- bittestandset, bittestandset64
If we are missing a function you feel should be included, please file an issue. Please keep in mind that some things are simply impossible to implement without compiler support.
Alternatives & Supplements
For overflow-safe integer operations (i.e., __builtin_*_overflow),
use safe-math.h.
For bswap/byteswap functions, you should really use endian.h, which depends on this module and handles endianness detection as well as providing easier to use APIs which integrate endianness detection logic.
For SIMD intrinsics (SSE, AVX, NEON, etc.), take a look at the SIMDe project.
For things which are effectively compiler hints (such as
__builtin_expect) as opposed to data manipulation functions, see
Hedley.
Areas for future work
Optimization
Performance should be pretty good but we're always open to shaving off a few operations, even if it means creating different variants for different compilers or architectures.
Creating implementations of one compiler's builtins using builtins from another is probably your best bet to improve performance. Another useful possibility is using architecture-specific builtins, or even embedded assembly, when they are available.
Architecture-specific builtins
GCC and MSVC both have lots of architecture-specific builtins. Especially GCC, which supports many architectures. If you come across one which is useful and could be implemented with a portable fallback, let us know.
Builtins from other compilers
We've looked at GCC, MSVC, and Clang, but we're happy to support builtins from other compilers, too.