Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What ABI, if any, restricts the size of [u]intmax_t?

Tags:

c

abi

c99

c11

Starting with the 1999 edition, the ISO C standard defines a standard header <stdint.h> which defines, among other things, the typedefs intmax_t and uintmax_t. These designate, respectively, "a (signed|unsigned) integer type capable of representing any value of any (signed|unsigned) integer type".

For example, if, as is typical, the widest signed and unsigned integer types are long long int and unsigned long long int, both of which are typically 64 bits, then intmax_t and uintmax_t might be defined in <stdint.h> as follows:

typedef long long int intmax_t;
typedef unsigned long long int uintmax_t;

There is a limited set of predefined signed and unsigned integer types, ranging from signed, unsigned, and plain char up to signed and unsigned long long int.

C99 and C11 also permit implementations to define extended integer types, which are distinct from any of the standard types and have names that are implementation-defined keywords.

Both gcc and clang, on some but not all targets, support types __int128 and unsigned __int128. These act like 128-bit integer types, but they are not treated as extended integer types, and the documentation for both compilers states that they do not support any extended integer types. Because these are not integer types as the Standard defines the term, the typedefs intmax_t and uintmax_t are for 64-bit types, not 128-bit types.

None of this violates the C standard (implementations are not required to have any extended integer types, and they're permitted to have arbitrary extensions as long as they don't break any strictly conforming programs). But it seems to me that it would make perfect sense for __int128 and unsigned __int128 to be treated as extended integer types, and for intmax_t and uintmax_t to be 128-bit types.

The rationale for not doing this is that changing the size of intmax_t and uintmax_t would be "an ABI-incompatible change".

The Clang C++ status page says, in footnote (5):

No compiler changes are required for an implementation such as Clang that does not provide any extended integer types. __int128 is not treated as an extended integer type, because changing intmax_t would be an ABI-incompatible change.

(Yes, this primarily discusses C++, but the rules are the same as for C.)

In a gcc bug report, the claim is made that:

sizeof(intmax_t) is fixed by various LP64 ABIs and cannot be changed

In both cases, no reference is given for this claim.

An x86_64 ABI document titled "System V Application Binary Interface, AMD64 Architecture Processor Supplement, Draft Version 0.99.6" does not mention intmax_t or uintmax_t, or even the <stdint.h> header. It does specify sizes and alignments for the predefined integer types (in Figure 3.1).

Finally, my question: Is the claim that the sizes of intmax_t and uintmax_t restricted by an ABI valid? If so, what ABI imposes such a requirement? (And, incidentally, why?)

(In my opinion, such a requirement, if it exists, is unwise. It defeats the purpose of the C standard's permission to define extended integer types, and the intended meaning of intmax_t and uintmax_t. It makes it much more difficult to use 128-bit integer types effectively on systems that support them, while falling back to narrower types on other systems.)

Update: In N2303, titled "intmax t, a way out", Jens Gustedt proposes tweaking the definitions of [u]intmax_t to permit adding extended integer types wider than long long without having to update [u]intmax_t. For example, intmax_t might be a typedef for long long, but the implementation could still provide, say, __int128 as an extended integer type.

References:

  • N1256, a draft of the C99 standard
  • N1570, a draft of the C11 standard
  • N2303, a proposal by Jens Gustedt
  • System V AMD64 ABI
like image 904
Keith Thompson Avatar asked Apr 28 '15 18:04

Keith Thompson


3 Answers

As Colonel Thirty Two notes, a compiler unilaterally making this change would break calls between compilation units that pass uintmax_t parameters or return uintmax_t values. Even though the SysV ABI doesn't define how these types are passed, as a matter of practicality maintaining their definitions is part of conforming to the platform ABI.

Even if it weren't for this ABI issue, a compiler still couldn't unilaterally make this change, because it would require matching changes to every targeted platform's C standard library. Specifically, it would at least require updates to the printf and scanf function family, imaxabs, imaxdiv, and strtoimax and strtoumax and their variants.

like image 187
Stephen Canon Avatar answered Oct 22 '22 20:10

Stephen Canon


Changing types like intmax_t and uintmax_t also changes the ABI of all the programs that use them, as they now refer to different types.

Say you have program A that uses a function in shared library B with a uintmax_t parameter. If GCC changes the definition of uintmax_t and A (but not B) gets recompiled, then uintmax_t in A and uintmax_t in B now refer to two different types, breaking the ABI.

like image 7
Colonel Thirty Two Avatar answered Oct 22 '22 22:10

Colonel Thirty Two


I think the key to understand here is that just because something is not documented in an ABI specification does not mean it is not part of an ABI. As soon as a type is used across a library boundry then it's properties become part of the ABI of that library.

By defining (u)intmax_t in a standard header and using them in functions of the standard library they become part of the ABI of that library whether they are included in any formal ABI specification or not.

This is especially an issue for Unix-like platforms where the C standard library is treated as part of the platform, not part of the compiler.

Now it would be possible to transition this. Printf uses macros for type specifiers, so those macros could be defined differently depending on the size of intmax_t. Macros could similarly be used to map the handful of functions in the standard library to different implementations but it's a bunch of extra work for questionable gains, so it's hardly surprising that gcc took the path of least resistance to adding the functionality they needed.

like image 4
plugwash Avatar answered Oct 22 '22 22:10

plugwash