Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 12 Jul 2013 19:14:39 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Tijl Coosemans <tijl@coosemans.org>
Cc:        svn-src-head@FreeBSD.org, svn-src-all@FreeBSD.org, src-committers@FreeBSD.org, David Chisnall <theraven@FreeBSD.org>, Bruce Evans <brde@optusnet.com.au>
Subject:   Re: svn commit: r253215 - head/lib/msun/src
Message-ID:  <20130712180753.E5131@besplex.bde.org>
In-Reply-To: <51DF14F9.50001@coosemans.org>
References:  <201307111741.r6BHf5gQ060844@svn.freebsd.org> <51DEFEF7.4080709@coosemans.org> <7D521907-4802-4141-9A5E-40EB157A5AEF@FreeBSD.org> <51DF0FA5.4050106@coosemans.org> <51DF14F9.50001@coosemans.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 11 Jul 2013, Tijl Coosemans wrote:

> On 2013-07-11 22:03, Tijl Coosemans wrote:
>> On 2013-07-11 21:36, David Chisnall wrote:
>>> On 11 Jul 2013, at 19:52, Tijl Coosemans <tijl@coosemans.org> wrote:
>>>>> @@ -227,8 +250,6 @@ double	expm1(double);
>>>>> double	fma(double, double, double);
>>>>> double	hypot(double, double);
>>>>> int	ilogb(double) __pure2;
>>>>> -int	(isinf)(double) __pure2;
>>>>> -int	(isnan)(double) __pure2;
>>>>
>>>> I think they should stay for the C90 case.
>>>
>>> That would completely defeat the point of this entire exercise and be
>>> redundant unless we aim to support a compiler that only supports C90
>>> and no GNU extensions, in which case you'll hit errors in cdefs.h,
>>> long before you get to this point in an include.
>>
>> isnan(double) is part of SUSv2. It should be visible when compiling with
>> -D_XOPEN_SOURCE=500. I think you need something like this:

>> #if (__BSD_VISIBLE || __XSI_VISIBLE <= 500) && __ISO_C_VISIBLE < 1999
>> int	isinf(double) __pure2;
>> int	isnan(double) __pure2;
>> #endif
>
> Actually this:
>
> #if (__BSD_VISIBLE || (defined(__XSI_VISIBLE) && __XSI_VISIBLE <= 500)) && __ISO_C_VISIBLE < 1999

Remove the __ISO_C_VISIBLE part, since this is not in C90.  This also fixes
a style bug (long line).

How can that work?  Even you forgot to restore the parentheses around
the functions, so the above has syntax errors.  Applications would
have to use parentheses to get the functions (or not include <math.h>,
but then it doesn't matther if it doesn't declare the functions).
Applications that forget to do this will get the macros instead of the
functions.  If the arg type is double, then the macro will work the
same as the functions.  Otherwise, it has different semantics, but
usually the same result except for signaling NaNs.  But does old XSI
really specify that parentheses must be used to get isnan() (or can
be used to get an isnan() function that is specified to exist)?  Old
BSD has almost no specification for isnan(), and there probably isn't
much old BSD code that carefully prevents use of the macro using
parentheses.  isnan()'s man page actually says that 3BSD introduced
isinf() and isnan() functions, but these have been superseded by the
macros.

I noticed some more problems in the implementation of these macros
and others:
- many or all of the __pure2's in the prototypes are wrong, since even
   the classification functions can have side effects for signaling
   NaNs.  It is impossible to avoid these side effects for extern
   functions in some cases, since the ABI gives them.  I think static
   inline functions must have the same results as extern functions,
   so compilers should pessimize inline functions as necessary to
   get the same bad results, but compilers don't do that.
- classification functions are specified to convert to the semantic
   type (remove any extra precision or exponent range) before classifying.

   For example, if x is a double slightly larger than sqrt(DBL_MAX), and
   if double expressions are evaluated in extra exponent range, then x*x
   is finite with the extra range but infinite in the semantic type.  So
   isinf(x*x) is true, and the implementation
   #define isinf(x) (fabs(x) == INFINITY) is invalid.  clang on x86 gets
   __builtin_isinf(x*x) this right as a side effect of its pessimization
   of fabs() to non-inline -- parameter passing to the extern fabs()
   converts to the semantic type.  Sometimes the arg is known not to have
   extra range, so no conversion is needed.

   isnan(x) can safely skip the conversion, at least on x86, since conversion
   doesn't change the classification of NaNs.  __builtin_isnan(x*x) for
   clang on x86 skips the conversion.

   __builtin_isinfinite(x*x) for clang on x86 is a combination of the above --
   isnan() with no conversion and !isinf() with pessimal conversion via
   non-inline fabs().

I couldn't find any case where a necessary conversion is not done.  Our
implementation using functions gives the necessary conversions including
ones that are broken for signaling NaNs.  But there is no problem for
with signaling NaNs for expressions like x*x, since the result of an
expression with almost any operator in it can't be a signaling NaN.

I think C11 has new mistakes for extra precision.  It specifies that
return reduces to the semantic type, like the classification macros
are required to do for their arg.  clang -std=c11 doesn't implement
this bug for at least:

 	#include <math.h>
 	double sq(double x) { return (x*x); }
 	double sq2(double x) { return (fabs(x*x); }

On i386 without SSE2 (so extra precision), this generates the same code
as with -std=c99.  Squaring x gives extra precision and exponent range.
This is not destroyed on return, so extra precision is not defeated by
writing the squaring operation as a function.  fabs() is inlined in both
cases, so it has little effect here (no effect unless x is NaN), but I
think even C99 doesn't permit this.  If fabs() were not inline, then
the ABI would force destruction of the extra precision and range when
it is called, and I think C99 requires conversion to the semantic type
for calls.

Bruce



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