Add <limits.h> integer width macros
diff mbox

Message ID alpine.DEB.2.20.1609131749040.9352@digraph.polyomino.org.uk
State New
Headers show

Commit Message

Joseph Myers Sept. 13, 2016, 5:49 p.m. UTC
TS 18661-1 defines macros for the width of integer types, intended for
use with the fromfp functions to convert from floating-point types to
integer types of any width, in any rounding mode and with control over
whether "inexact" is raised.  Such macros are, of course, more
generally useful than just with those functions.

Those macros are added to <limits.h> and <stdint.h>.  This patch adds
the <limits.h> macros to glibc's header, with the <stdint.h> ones
intended to be added in a separate patch (which would add to the NEWS
entry created by this patch).  I've also added these macros to GCC's
headers for GCC 7, but definitions in glibc's <limits.h> are still
useful for older GCC, for non-GNU compilers and for when it's
_GNU_SOURCE rather than __STDC_WANT_IEC_60559_BFP_EXT__ that implies
the macros should be defined since the GCC header only considers
__STDC_WANT_IEC_60559_BFP_EXT__ (and for glibc systems, the
definitions in GCC's <stdint.h> will only be used with
-ffreestanding).

Tested for x86_64 and x86.

2016-09-13  Joseph Myers  <joseph@codesourcery.com>

	* include/limits.h: Define
	__GLIBC_INTERNAL_STARTING_HEADER_IMPLEMENTATION and include
	<bits/libc-header-start.h> instead of including <features.h>.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (CHAR_WIDTH): New macro.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (SCHAR_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (UCHAR_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (SHRT_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (USHRT_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (INT_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (UINT_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (LONG_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (ULONG_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (LLONG_WIDTH): Likewise.
	[__GLIBC_USE (IEC_60559_BFP_EXT)] (ULLONG_WIDTH): Likewise.
	* manual/lang.texi (Width of Type): Document these macros.
	* stdlib/tst-width.c: New file.
	* stdlib/Makefile (tests): Add tst-width.

Comments

Paul Eggert Sept. 14, 2016, 7:07 p.m. UTC | #1
On 09/13/2016 10:49 AM, Joseph Myers wrote:
> +#  define UINT_WIDTH 32

Dumb question: In the latest draft I could find 
<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1899.pdf>, these 
macros are supposed to be expressions of type size_t. Was this changed 
from 'size_t' to 'int' in the final standard? That would make sense, as 
these values are likely to be used in shift-width contexts where 
negative numbers can be useful.
Joseph Myers Sept. 14, 2016, 7:58 p.m. UTC | #2
On Wed, 14 Sep 2016, Paul Eggert wrote:

> On 09/13/2016 10:49 AM, Joseph Myers wrote:
> > +#  define UINT_WIDTH 32
> 
> Dumb question: In the latest draft I could find
> <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1899.pdf>, these macros are
> supposed to be expressions of type size_t. Was this changed from 'size_t' to
> 'int' in the final standard? That would make sense, as these values are likely
> to be used in shift-width contexts where negative numbers can be useful.

That's an independent proposal; it has nothing to do with the feature in 
TS 18661-1 (and presumably won't be adopted, since TS 18661-1 is likely to 
be merged into C2X).  TS 18661-1 does not specify the types of these 
macros, but int seems the most natural.
Paul Eggert Sept. 17, 2016, 12:32 a.m. UTC | #3
Thanks for explaining. In that case, the patch looks good; thanks. Also, 
I created a Gnulib module limits-h with a similar effect; see:

http://git.savannah.gnu.org/cgit/gnulib.git/commit/?id=0d12d16ab30cc5581ee26a3877351ac3181bca81

It is perhaps worth mentioning that the new definition of CHAR_WIDTH 
collides with Emacs's use of that symbol for its own macro. The new 
<limits.h> CHAR_WIDTH will be visible because Emacs defines _GNU_SOURCE. 
The typical symptoms are a warning while compiling Emacs and that Emacs 
still builds and runs correctly. I installed a change to Emacs so that 
this problem won't occur in future Emacs versions (after Emacs 25). This 
sort of thing happens every now and then and is not a significant 
obstacle to the glibc patch.
Florian Weimer Sept. 17, 2016, 4:55 p.m. UTC | #4
* Joseph Myers:

> +That expression includes padding bits as well as value and sign bits.
> +On all systems supported by @theglibc{}, standard integer types do not
> +have any padding bits.

I note that there is no width macro for _Bool, which is included among
the standard integer types (§6.2.5/6: “The type _Bool and the unsigned
integer types […] are the /standard unsigned integer types/.”;
§6.2.5/7: “The standard signed integer types and standard unsigned
integer types are collectively alled the /standard integer types/.”;
emphasis in the original).

Anyway, Bool has padding bits with GCC, so the remark quoted above is
technically wrong.  This can be seen with this code

int
f(_Bool *f)
{
  return *f & 2;
}

Which compiles to this optimized code with -O2:

f:
	xorl	%eax, %eax
	ret

And to this with -O1:

f:
	movzbl	(%rdi), %eax
	andl	$2, %eax
	ret


I think that with GCC, _Bool even has representations.  Not that I'm
happy about that.
Florian Weimer Sept. 18, 2016, 6:21 p.m. UTC | #5
* Florian Weimer:

> I think that with GCC, _Bool even has representations.  Not that I'm
> happy about that.

I meant to write ‘trap representation’.
Joseph Myers Sept. 19, 2016, 12:27 p.m. UTC | #6
On Sat, 17 Sep 2016, Florian Weimer wrote:

> Anyway, Bool has padding bits with GCC, so the remark quoted above is
> technically wrong.  This can be seen with this code

I've added "other than @code{_Bool}" to the documentation in the patch I 
committed.
Paul Eggert Oct. 4, 2016, 3:07 p.m. UTC | #7
On 09/16/2016 05:32 PM, Paul Eggert wrote:
> the patch looks good; thanks.

Unfortunately I did not check the patch thoroughly enough, as the patch 
turns out to define LONG_WIDTH incorrectly on i686 with 
gcc-6.2.1-2.fc26.i686, and this causes grep's 'make check' to fail 
(luckily these are side tests; grep itself works fine). See:

https://bugzilla.redhat.com/show_bug.cgi?id=1381582
Florian Weimer Oct. 4, 2016, 3:28 p.m. UTC | #8
On 10/04/2016 05:07 PM, Paul Eggert wrote:
> On 09/16/2016 05:32 PM, Paul Eggert wrote:
>> the patch looks good; thanks.
>
> Unfortunately I did not check the patch thoroughly enough, as the patch
> turns out to define LONG_WIDTH incorrectly on i686 with
> gcc-6.2.1-2.fc26.i686, and this causes grep's 'make check' to fail
> (luckily these are side tests; grep itself works fine). See:
>
> https://bugzilla.redhat.com/show_bug.cgi?id=1381582

See comment 5 there.  The problem seems to be that GCC <limits.h> is 
included first, followed by glibc <limits.h>, which then tries to get 
the definitions from GCC.  But at this point, nothing happens because 
the GCC include guards cause the GCC header to be skipped.

The proposed fix doesn't seem right, it would need checks for GCC and 
the GCC version.

Thanks,
Florian

Patch
diff mbox

diff --git a/NEWS b/NEWS
index ba1ec71..0ea6bfa 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,10 @@  Version 2.25
   fesetexcept, fetestexceptflag, fegetmode and fesetmode functions,
   the femode_t type and the FE_DFL_MODE macro.
 
+* Integer width macros from TS 18661-1:2014 are added to <limits.h>:
+  CHAR_WIDTH, SCHAR_WIDTH, UCHAR_WIDTH, SHRT_WIDTH, USHRT_WIDTH, INT_WIDTH,
+  UINT_WIDTH, LONG_WIDTH, ULONG_WIDTH, LLONG_WIDTH, ULLONG_WIDTH.
+
 * The <sys/quota.h> header now includes the <linux/quota.h> header.  Support
   for the Linux quota interface which predates kernel version 2.4.22 has
   been removed.
diff --git a/include/limits.h b/include/limits.h
index 5add8fc..93cac49 100644
--- a/include/limits.h
+++ b/include/limits.h
@@ -22,7 +22,8 @@ 
 #ifndef _LIBC_LIMITS_H_
 #define _LIBC_LIMITS_H_	1
 
-#include <features.h>
+#define __GLIBC_INTERNAL_STARTING_HEADER_IMPLEMENTATION
+#include <bits/libc-header-start.h>
 
 
 /* Maximum length of any multibyte character in any locale.
@@ -138,6 +139,54 @@ 
 # endif
 #endif
 
+/* The integer width macros are not defined by GCC's <limits.h> before
+   GCC 7, or if _GNU_SOURCE rather than
+   __STDC_WANT_IEC_60559_BFP_EXT__ is used to enable this feature.  */
+#if __GLIBC_USE (IEC_60559_BFP_EXT)
+# ifndef CHAR_WIDTH
+#  define CHAR_WIDTH 8
+# endif
+# ifndef SCHAR_WIDTH
+#  define SCHAR_WIDTH 8
+# endif
+# ifndef UCHAR_WIDTH
+#  define UCHAR_WIDTH 8
+# endif
+# ifndef SHRT_WIDTH
+#  define SHRT_WIDTH 16
+# endif
+# ifndef USHRT_WIDTH
+#  define USHRT_WIDTH 16
+# endif
+# ifndef INT_WIDTH
+#  define INT_WIDTH 32
+# endif
+# ifndef UINT_WIDTH
+#  define UINT_WIDTH 32
+# endif
+# if LONG_MAX == 0x7fffffffL
+#  ifndef LONG_WIDTH
+#   define LONG_WIDTH 32
+#  endif
+#  ifndef ULONG_WIDTH
+#   define ULONG_WIDTH 32
+#  endif
+# else
+#  ifndef LONG_WIDTH
+#   define LONG_WIDTH 64
+#  endif
+#  ifndef ULONG_WIDTH
+#   define ULONG_WIDTH 64
+#  endif
+# endif
+# ifndef LLONG_WIDTH
+#  define LLONG_WIDTH 64
+# endif
+# ifndef ULLONG_WIDTH
+#  define ULLONG_WIDTH 64
+# endif
+#endif /* Use IEC_60559_BFP_EXT.  */
+
 #ifdef	__USE_POSIX
 /* POSIX adds things to <limits.h>.  */
 # include <bits/posix1_lim.h>
diff --git a/manual/lang.texi b/manual/lang.texi
index 7f8a368..ea1a96b 100644
--- a/manual/lang.texi
+++ b/manual/lang.texi
@@ -653,6 +653,56 @@  sizeof (@var{type}) * CHAR_BIT
 @end smallexample
 @end table
 
+That expression includes padding bits as well as value and sign bits.
+On all systems supported by @theglibc{}, standard integer types do not
+have any padding bits.  TS 18661-1:2014 defines additional macros for
+the width of integer types (the number of value and sign bits); these
+macros can also be used in @code{#if} preprocessor directives, whereas
+@code{sizeof} cannot.  The following macros are defined in
+@file{limits.h}.
+
+@table @code
+@comment limits.h
+@comment ISO
+@item CHAR_WIDTH
+@comment limits.h
+@comment ISO
+@itemx SCHAR_WIDTH
+@comment limits.h
+@comment ISO
+@itemx UCHAR_WIDTH
+@comment limits.h
+@comment ISO
+@itemx SHRT_WIDTH
+@comment limits.h
+@comment ISO
+@itemx USHRT_WIDTH
+@comment limits.h
+@comment ISO
+@itemx INT_WIDTH
+@comment limits.h
+@comment ISO
+@itemx UINT_WIDTH
+@comment limits.h
+@comment ISO
+@itemx LONG_WIDTH
+@comment limits.h
+@comment ISO
+@itemx ULONG_WIDTH
+@comment limits.h
+@comment ISO
+@itemx LLONG_WIDTH
+@comment limits.h
+@comment ISO
+@itemx ULLONG_WIDTH
+
+These are the widths of the types @code{char}, @code{signed char},
+@code{unsigned char}, @code{short int}, @code{unsigned short int},
+@code{int}, @code{unsigned int}, @code{long int}, @code{unsigned long
+int}, @code{long long int} and @code{unsigned long long int},
+respectively.
+@end table
+
 @node Range of Type
 @subsection Range of an Integer Type
 @cindex integer type range
diff --git a/stdlib/Makefile b/stdlib/Makefile
index fc6f23d..ccc9aae 100644
--- a/stdlib/Makefile
+++ b/stdlib/Makefile
@@ -77,7 +77,7 @@  tests		:= tst-strtol tst-strtod testmb testrand testsort testdiv   \
 		   tst-tininess tst-strtod-underflow tst-tls-atexit	    \
 		   tst-setcontext3 tst-tls-atexit-nodelete		    \
 		   tst-strtol-locale tst-strtod-nan-locale tst-strfmon_l    \
-		   tst-quick_exit tst-thread-quick_exit
+		   tst-quick_exit tst-thread-quick_exit tst-width
 tests-static	:= tst-secure-getenv
 ifeq ($(have-cxx-thread_local),yes)
 CFLAGS-tst-quick_exit.o = -std=c++11
diff --git a/stdlib/tst-width.c b/stdlib/tst-width.c
new file mode 100644
index 0000000..4972c8d
--- /dev/null
+++ b/stdlib/tst-width.c
@@ -0,0 +1,87 @@ 
+/* Test integer width macros.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <limits.h>
+#include <stdio.h>
+
+#define CHECK_WIDTH(TYPE, MAX, WIDTH)					\
+  do									\
+    {									\
+      if ((MAX >> ((TYPE) -1 < 0 ? (WIDTH - 2) : (WIDTH - 1))) != 1)	\
+	{								\
+	  puts ("bad width of " #TYPE);					\
+	  result = 1;							\
+	}								\
+      else								\
+	puts ("width of " #TYPE " OK");					\
+    }									\
+  while (0)
+
+static int
+do_test (void)
+{
+  int result = 0;
+#ifndef CHAR_WIDTH
+# error "missing CHAR_WIDTH"
+#endif
+  CHECK_WIDTH (char, CHAR_MAX, CHAR_WIDTH);
+#ifndef SCHAR_WIDTH
+# error "missing SCHAR_WIDTH"
+#endif
+  CHECK_WIDTH (signed char, SCHAR_MAX, SCHAR_WIDTH);
+#ifndef UCHAR_WIDTH
+# error "missing UCHAR_WIDTH"
+#endif
+  CHECK_WIDTH (unsigned char, UCHAR_MAX, UCHAR_WIDTH);
+#ifndef SHRT_WIDTH
+# error "missing SHRT_WIDTH"
+#endif
+  CHECK_WIDTH (signed short, SHRT_MAX, SHRT_WIDTH);
+#ifndef USHRT_WIDTH
+# error "missing USHRT_WIDTH"
+#endif
+  CHECK_WIDTH (unsigned short, USHRT_MAX, USHRT_WIDTH);
+#ifndef INT_WIDTH
+# error "missing INT_WIDTH"
+#endif
+  CHECK_WIDTH (signed int, INT_MAX, INT_WIDTH);
+#ifndef UINT_WIDTH
+# error "missing UINT_WIDTH"
+#endif
+  CHECK_WIDTH (unsigned int, UINT_MAX, UINT_WIDTH);
+#ifndef LONG_WIDTH
+# error "missing LONG_WIDTH"
+#endif
+  CHECK_WIDTH (signed long, LONG_MAX, LONG_WIDTH);
+#ifndef ULONG_WIDTH
+# error "missing ULONG_WIDTH"
+#endif
+  CHECK_WIDTH (unsigned long, ULONG_MAX, ULONG_WIDTH);
+#ifndef LLONG_WIDTH
+# error "missing LLONG_WIDTH"
+#endif
+  CHECK_WIDTH (signed long long, LLONG_MAX, LLONG_WIDTH);
+#ifndef ULLONG_WIDTH
+# error "missing ULLONG_WIDTH"
+#endif
+  CHECK_WIDTH (unsigned long long, ULLONG_MAX, ULLONG_WIDTH);
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"