Skip to content
  • Thomas Petazzoni's avatar
    toolchain: add BR2_TOOLCHAIN_HAS_{SYNC_x, ATOMIC} hidden booleans · 6856e417
    Thomas Petazzoni authored
    Currently, Buildroot provides one BR2_ARCH_HAS_ATOMICS boolean option
    to indicate whether the architecture supports atomic operations or
    not. However, the reality of atomic operations support is much more
    complicated and requires more than one option to be expressed
    properly.
    
    There are in fact two types of atomic built-ins provided by gcc:
    
     (1) The __sync_*() family of functions, which have been in gcc for a
         long time (probably gcc 4.1). They are available in variants
         operating on 1-byte, 2-byte, 4-byte and 8-byte integers. Some
         architectures implement a number of variants, some do not
         implement any, some implement all of them.
    
         They are now considered "legacy" by the gcc developers but are
         nonetheless still being used by a significant number of userspace
         libraries and applications.
    
         https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
    
    
    
     (2) The __atomic_*() family of functions, which have been introduced
         in gcc 4.7. They have been introduced in order to support C++11
         atomic operations. In gcc 4.8, they are available on all
         architectures, either built-in or in the libatomic library part
         of the gcc runtime (in which case the application needs to be
         linked with -latomic). In gcc 4.7, the __atomic_*() intrinsics
         are only supported on certain architectures, since libatomic did
         not exist at the time.
    
    For (1), a single BR2_ARCH_HAS_ATOMICS is not sufficient, because
    depending on the architecture, some variants may or may not be
    available. Setting BR2_ARCH_HAS_ATOMICS to false as soon as one of the
    variant is missing would cause a large number of packages to become
    unavailable, even if they in fact use only more common variants
    available on a large number of architectures. For this reason, we've
    chosen to introduce four new Config.in options:
    
     - BR2_TOOLCHAIN_HAS_SYNC_1
     - BR2_TOOLCHAIN_HAS_SYNC_2
     - BR2_TOOLCHAIN_HAS_SYNC_3
     - BR2_TOOLCHAIN_HAS_SYNC_4
    
    Which indicate whether the toolchain support 1-byte, 2-byte, 4-byte
    and 8-byte __sync_*() built-ins respectively.
    
    For (2), we introduce a BR2_TOOLCHAIN_HAS_ATOMIC, which indicates if
    the __atomic_*() built-ins are available. Note that it is up to the
    package to link with -latomic when gcc is >= 4.8. Since __atomic_*()
    intrinsics for all sizes are supported starting
    
    We conducted a fairly large analysis about various architectures
    supported by Buildroot, as well as with a number of different
    toolchains, to check which combinations support which variant. To do,
    we linked the following program with various toolchains:
    
    int main(void)
    {
    	uint8_t a;
    	uint16_t b;
    	uint32_t c;
    	uint64_t d;
    
    	__sync_fetch_and_add(&a, 3);
    	__sync_fetch_and_add(&b, 3);
    	__sync_fetch_and_add(&c, 3);
    	__sync_fetch_and_add(&d, 3);
    
    	__sync_val_compare_and_swap(&a, 1, 2);
    	__sync_val_compare_and_swap(&b, 1, 2);
    	__sync_val_compare_and_swap(&c, 1, 2);
    	__sync_val_compare_and_swap(&d, 1, 2);
    
    	__atomic_add_fetch(&a, 3, __ATOMIC_RELAXED);
    	__atomic_add_fetch(&b, 3, __ATOMIC_RELAXED);
    	__atomic_add_fetch(&c, 3, __ATOMIC_RELAXED);
    	__atomic_add_fetch(&d, 3, __ATOMIC_RELAXED);
    
    	__atomic_compare_exchange_n(&a, &a, 2, 1,  __ATOMIC_RELAXED,  __ATOMIC_RELAXED);
    	__atomic_compare_exchange_n(&b, &b, 2, 1,  __ATOMIC_RELAXED,  __ATOMIC_RELAXED);
    	__atomic_compare_exchange_n(&c, &c, 2, 1,  __ATOMIC_RELAXED,  __ATOMIC_RELAXED);
    	__atomic_compare_exchange_n(&d, &d, 2, 1,  __ATOMIC_RELAXED,  __ATOMIC_RELAXED);
    
    	return 0;
    }
    
    And looked at which symbols were unresolved. For the __atomic_*()
    ones, we tested with and without -latomic to see which variants are
    built-in, which variants require libatomic. This testing effort has
    led to the following results:
    
                    __sync       __atomic    gcc
                   1  2  4  8    1  2  4  8
    ARC            Y  Y  Y  -    Y  Y  Y  L   4.8 [with BR2_ARC_ATOMIC_EXT]
    ARC            -  -  -  -    L  L  L  L   4.8 [without BR2_ARC_ATOMIC_EXT]
    ARM            Y  Y  Y  X    Y  Y  Y  Y   4.8, 4.7
    ARM            Y  Y  Y  -                 4.5
    AArch64        Y  Y  Y  Y    Y  Y  Y  Y   4.9, 5.1
    Bfin           -  -  Y  -                 4.3
    i386 (i386)    -  -  -  -    L  L  L  L   4.9
    i386 (i486..)  Y  Y  Y  -    L  L  L  L   4.9 [i486, c3, winchip2, winchip-c6]
    i386 (> i586)  Y  Y  Y  Y    L  L  L  L   4.9
    Microblaze     -  -  Y  -    L  L  Y  L   4.9
    MIPS           Y  Y  Y  -    Y  Y  Y  L   4.9
    MIPS64         Y  Y  Y  Y    Y  Y  Y  Y   4.9
    NIOS 2         Y  Y  Y  -    Y  Y  Y  L   4.9, 5.2
    PowerPC        Y  Y  Y  -    Y  Y  Y  L   4.9
    SuperH         Y  Y  Y  -    Y  Y  Y  L   4.9
    SPARC          -  -  -  -    L  L  L  L   4.9
    SPARC64        Y  Y  Y  Y    Y  Y  Y  Y   4.9
    x86_64         Y  Y  Y  Y    Y  Y  Y  Y   4.7, 4.9
    Xtensa         Y  Y  Y  -    Y  Y  Y  Y   4.9
    
    Notes:
    
     * __atomic built-ins appeared in gcc 4.7, so for toolchais older than
       that, the __atomic column is empty.
    
     * Y means 'supported built-in'
    
     * L means 'supported via linking to libatomic' (only for __atomic
       functions)
    
     * X indicates a very special case for 8 bytes __sync built-ins on
       ARM. On ARMv7, there is no problem, starting from gcc 4.7, the
       __sync built-in for 8 bytes integers is implemented, fully in
       userspace. For cores < ARMv7, doing a 8 bytes atomic operation
       requires help from the kernel. Unfortunately, the libgcc code
       implementing this uses the __write() function to display an error,
       and this function is internal to glibc. Therefore, if you're using
       glibc everything is fine, but if you're using uClibc or musl, you
       cannot link an application that uses 8 bytes __sync
       operations. This has been fixed as part of gcc PR68095, merged in
       the gcc 5 branch but not yet part of any gcc release.
    
     * - means not supported
    
    This commit only introduces the new options. Follow-up commits will
    progressively change the packages using BR2_ARCH_HAS_ATOMICS to use
    the appropriate BR2_TOOLCHAIN_HAS_SYNC_x or BR2_TOOLCHAIN_HAS_ATOMIC
    until the point where BR2_ARCH_HAS_ATOMICS can be removed.
    
    Signed-off-by: default avatarThomas Petazzoni <thomas.petazzoni@free-electrons.com>
    6856e417