Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 4 Dec 2017 14:43:19 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Allan Jude <allanjude@freebsd.org>
Cc:        src-committers@freebsd.org, svn-src-all@freebsd.org,  svn-src-head@freebsd.org
Subject:   Re: svn commit: r326506 - in head/sys/contrib/zstd/lib: common compress
Message-ID:  <20171204134316.O1755@besplex.bde.org>
In-Reply-To: <201712040116.vB41GQ41078524@repo.freebsd.org>
References:  <201712040116.vB41GQ41078524@repo.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 4 Dec 2017, Allan Jude wrote:

> Log:
>  Use __has_builtin() to ensure clz and clzll builtins are available
>
>  The existing check of the GCC version number is not sufficient

It also checked a wrong version number, and still doesn't.

>  This fixes the build on sparc64 in preparation for integrating ZSTD into
>  the kernel for ZFS and Crash Dumps.

This doesn't really work.  __has_builtin() is a dummy (always 0) in
at least gcc-4.2.1 and earlier versions of gcc, and is broken as
designed in compilers that implement it.  It just tells you if the
compiler supports the builtin, but you don't want to know about the
builtin unless the builtin will generate better code for the target
arch than your alternative.  Using __has_builtin() is especially wrong
in the kernel and other pure freestanding cases.  Then the builtin
generates a libcall to nonexistent function unless the target arch
supports a better inline alternative.  Nonexistent functions are never
better alternatives than yours.

> Modified: head/sys/contrib/zstd/lib/common/bitstream.h
> ==============================================================================
> --- head/sys/contrib/zstd/lib/common/bitstream.h	Mon Dec  4 01:14:17 2017	(r326505)
> +++ head/sys/contrib/zstd/lib/common/bitstream.h	Mon Dec  4 01:16:26 2017	(r326506)
> @@ -175,7 +175,7 @@ MEM_STATIC unsigned BIT_highbit32 (register U32 val)
>         unsigned long r=0;
>         _BitScanReverse ( &r, val );
>         return (unsigned) r;
> -#   elif defined(__GNUC__) && (__GNUC__ >= 3)   /* Use GCC Intrinsic */

This version check is very broken.  gcc-3.3.3 doesn't have __builtin_clz(),
but passes the check.

> +#   elif defined(__GNUC__) && (__GNUC__ >= 3) && __has_builtin(__builtin_clz)   /* Use GCC Intrinsic */

gcc-4.2.1 doesn't have __builtin_clz() on x86, but not __has_builtin().
sys/cdefs.h defines __has_builtin() as 0 unless __has_builtin is defined.

The first part of the test is now usually just an obfuscation.  It
still uses a wrong version number and a redumdant test that __GNUC__
is defined, but when (__GNUC__ < 3), __has_builtin() is usually 0
and gives the same result of 0.  It is only in the unusual (unsupported)
case where the compiler doesn't pretend to be gcc but defines __has_builtin()
that the first part of the check has an effect, and then its effect is to
break the second part.

sys/cdefs.h gives an example of a similar but presumably-correct version
check.  For using __builtin_unreachable(), it doesn't trust __has_builtin(),
but checks that the version is >= 4.6.  4.6 is fine-grained enough to have
a chance of being correct.

>         return 31 - __builtin_clz (val);

I think this is only for userland, so it is not broken when the builtin
reduce a libcall.  The libcall is presumably to __clzdi2().  The kernel
doesn't have this, but libgcc.a does.

> #   else   /* Software version */
>         static const unsigned DeBruijnClz[32] = { 0,  9,  1, 10, 13, 21,  2, 29,

This is unreachable when the libcall is used.

If optimizing this is actually important, then so not is pessimizing it on
arches with the builtin implemented in hardware but __has_builtin() not
implemented.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20171204134316.O1755>