This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
libm error handling
- From: "Joseph S. Myers" <joseph at codesourcery dot com>
- To: <libc-alpha at sourceware dot org>
- Date: Tue, 30 Apr 2013 16:30:43 +0000
- Subject: libm error handling
Different libm error handling approaches are selected by the
_LIB_VERSION global variable. This defaults to _POSIX_ (selected by
CPPFLAGS-s_lib_version.c := -D_POSIX_MODE in math/Makefile, used to
compile sysdeps/ieee754/s_lib_version.c), may be set to _IEEE_ by
linking with -lieee (math/ieee-math.c) and otherwise may be set
explicitly by the user. The possible values are: _IEEE_, _SVID_,
_XOPEN_, _POSIX_, _ISOC_, and the following notes describe the
differences between the versions. Comments are welcome, particularly
on the proposed principles / changes at the end. This analysis
ignores the special IA64 error handling
(ports/sysdeps/ia64/fpu/libm_error.c) on the basis that it's for IA64
maintainers to follow any changes in this area made elsewhere in
glibc.
What follows is specifically about real functions; complex functions
are not required by ISO C to set errno (regardless of the
math_errhandling value) and generally do not do so (because they use
the __ieee754_* versions of real functions internally). Furthermore,
everything about matherr is limited to functions that were in the
SVID, or float and long double variants of those; newer functions do
not attempt to support this legacy error handling mechanism, only
exceptions and errno.
In general, the value _IEEE_ means "do not set errno, or call matherr;
do not use the __kernel_standard wrapper that does those things at
all". The "do not set errno" part is not guaranteed, since some
functions directly set errno without checking _LIB_VERSION. The "do
not use __kernel_standard" part *is* guaranteed, and so "do not call
matherr" is also guaranteed.
In general, _POSIX_ (the default) means that both exceptions and errno
are used, but matherr is not called.
The other values seem more poorly defined. Do exceptions get raised
for them? In some cases, yes, but in others not, depending on whether
the conditions for using the wrapper are based on the inputs to the
function, or on the result of the __ieee754_* version of the function.
Is matherr called? Generally yes, even for _ISOC_ where it seems
inappropriate. Is errno set? The conditions in __kernel_standard
generally set it only for _POSIX_, or if matherr returns 0, but just
as _IEEE_ doesn't guarantee not setting errno, so the other values do
not guarantee it.
More specifically, _SVID_ and _XOPEN_ may both be interpreted as
meaning that matherr should be called (and errno not necessarily set
unless matherr returns 0), and _SVID_ as meaning that an error message
is printed to stderr if matherr returns zero. In addition, _SVID_
means that return values are typically HUGE (the largest finite float)
instead of infinity or NaN.
The following cases have other special handling for particular
_LIB_VERSION values, beyond the general description above:
* atan2 (zero, zero) handles _SVID_ specially.
* Bessel function TLOSS errors are not reported for _IEEE_ or _POSIX_.
* lgamma does not set signgam for _ISOC_.
* scalb (removed in current POSIX, not in ISO C) uses a different
implementation for _SVID_.
* There are various peculiarities of error handling in particular
cases, conditioned on _SVID_ or _POSIX_, in __kernel_standard.
Now for proposed principles and changes:
* With the proposed removal of _SVID_SOURCE as a supported feature
test macro, the _SVID_ and _XOPEN_ error handling modes should be
considered legacy modes for existing binaries only. Both these
modes clearly predate ANSI C89 / ISO C90 (which specifies error
handling via errno), which is generally the oldest API supported by
glibc now. Whether floating-point exceptions do get raised in
particular cases in those modes should not be significant, as any
code using them should not also try to use floating-point exceptions.
Making matherr / _LIB_VERSION into compat symbols might be hard,
however; I don't know if that works for symbols that the user's
program defines itself, and you'd have the issue of new programs
linked with -lieee (change -lieee to define a constructor that calls
a new interface to change the error handling mode, if you don't
obsolete use of -lieee as well?).
* The _ISOC_ mode should also be considered legacy. It's not
documented, and not a sensible approach for controlling signgam use;
the present situation, where if a user defines a variable called
signgam it gets modified by lgamma in a program built with -std=c99,
is simply a bug, as signgam is in the user's namespace for C99/C11,
and ISO C doesn't include any requirement to set _LIB_VERSION to
_ISOC_.
As far as I can tell, POSIX only reserves the identifier signgam
with external linkage when math.h is included with _XOPEN_SOURCE
defined appropriately to enable XSI extensions, not otherwise (there
is a long list of symbols with external linkage that are always
reserved, and it does not include signgam). So I think the
following should work for a fix: the lgamma symbol versions
presently in use are made into compat symbol versions, for existing
binaries where lgamma is expected to set signgam (except for
_ISOC_). The new version of the lgamma symbol does not set signgam,
while a separate __lgamma_xsi function is defined that does set
signgam, and is used when math.h is included with appropriate
feature test macros defined.
Otherwise, the most sensible handling of _ISOC_ is probably like
_POSIX_ (meaning errno is set, TLOSS errors do not occur, and
matherr is not called).
* As a corollary of the above, and given _ISOC_ treated like _POSIX_
for TLOSS errors, bugs 6805 and 6806 (Bessel functions not raising
underflow exception on TLOSS errors) could be closed as such errors
would now only be considered to be relevant to legacy binaries not
caring about floating-point exceptions.
* The most sensible way to control errno setting is not -lieee or
_LIB_VERSION = _IEEE_, it's -fno-math-errno. It would make sense
for that to predefine __NO_MATH_ERRNO__ in GCC, and then to export
the no-errno versions of functions under appropriate __*_noerrno (or
similar) names - it's inevitably going to be a bit faster for
programs to call the functions directly without needing runtime
checks on _LIB_VERSION.
* Existing tests of _LIB_VERSION should where possible move to using
appropriate macros relating to what is being logically tested
(e.g. support for non-exception error handling); most cases in
__kernel_standard have pretty standard code much more of which could
go in a macro. Such macros could also use __glibc_likely /
__glibc_unlikely to reflect that _POSIX_ is by far the most likely
_LIB_VERSION setting.
--
Joseph S. Myers
joseph@codesourcery.com