什么 ABI(如果有的话)限制 [u]intmax_t 的大小?

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

从 1999 版开始,ISO C 标准定义了一个标准 header <stdint.h>,其中定义了 typedef intmax_tuintmax_t。这些分别指定 "a (signed|unsigned) integer type capable of representing any value of any (signed|unsigned) integer type".

例如,如果通常情况下最宽的有符号和无符号整数类型是 long long intunsigned long long int,它们通常都是 64 位,那么 intmax_tuintmax_t 可以在 <stdint.h> 中定义如下:

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

有一组有限的预定义有符号和无符号整数类型,范围从 signedunsigned 和普通 charsignedunsigned long long int.

C99 和 C11 还允许实现定义 扩展整数类型 ,它们不同于任何标准类型,并且具有 implementation-defined 关键字的名称。

gcc 和 clang 在一些但不是所有目标上都支持类型 __int128unsigned __int128。它们的行为类似于 128 位整数类型,但它们 被视为 扩展整数类型 ,并且两个编译器的文档都指出它们不支持任何扩展整数类型。因为这些不是标准定义术语的 整数类型 ,所以 typedef intmax_tuintmax_t 适用于 64 位类型,而不是 128 位类型。

None 这违反了 C 标准(实现不需要任何扩展整数类型,并且只要它们不破坏任何严格符合的程序,它们就可以具有任意扩展) .但在我看来,将 __int128unsigned __int128 视为扩展整数类型以及将 intmax_tuintmax_t 视为 128 位类型是完全合理的.

这样做的理由是改变intmax_tuintmax_t的大小将是"an ABI-incompatible change"。

Clang C++ status page 在脚注 (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.

(是的,这主要讨论 C++,但规则与 C 相同。)

gcc bug report 中,声明是:

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

在这两种情况下,都没有为此声明提供参考。

标题为"System V Application Binary Interface, AMD64 Architecture Processor Supplement, Draft Version 0.99.6"的x86_64 ABI document没有提到intmax_tuintmax_t,甚至<stdint.h>header。它确实指定了预定义整数类型的大小和对齐方式(在图 3.1 中)。

最后,我的问题是:intmax_tuintmax_t 的大小受 ABI 限制的说法是否有效?如果是这样,是什么 ABI 强加了这样的要求? (顺便说一句,为什么?)

(在我看来,这样的要求,如果存在的话,也是不明智的。它违背了C标准允许定义扩展整数类型的目的,以及intmax_t和[=14的预期含义=]。这使得在支持它们的系统上有效地使用 128 位整数类型变得更加困难,同时在其他系统上退回到更窄的类型。)

更新:在标题为 "intmax t, a way out" 的 N2303 中,Jens Gustedt 建议调整 [u]intmax_t 的定义以允许添加比 [=47 更宽的扩展整数类型=] 而无需更新 [u]intmax_t。例如,intmax_t 可能是 long long 的类型定义,但实现仍然可以提供 __int128 作为 扩展整数类型 .

参考文献:

更改 intmax_tuintmax_t 等类型也会更改所有使用它们的程序的 ABI,因为它们现在引用不同的类型。

假设您有一个程序 A,它使用共享库 B 中的一个函数和一个 uintmax_t 参数。如果 GCC 更改了 uintmax_t 的定义并且 A(但不是 B)被重新编译,那么 A 中的 uintmax_t 和 B 中的 uintmax_t 现在指的是两种不同的类型,从而破坏了 ABI。

正如三十二上校所说,单方面进行此更改的编译器会中断传递 uintmax_t 参数或 return uintmax_t 值的编译单元之间的调用。尽管 SysV ABI 没有定义如何传递这些类型,但实际上维护它们的定义是符合平台 ABI 的一部分。

即使不是这个 ABI 问题,编译器仍然无法单方面进行此更改,因为它需要对每个目标平台的 C 标准库进行匹配更改。具体来说,它至少需要更新 printfscanf 函数系列、imaxabsimaxdivstrtoimaxstrtoumax 以及它们的变体。

我认为这里要理解的关键是,ABI 规范中没有记录某些内容并不意味着它不是 ABI 的一部分。一旦跨库边界使用类型,它的属性就会成为该库 ABI 的一部分。

通过在标准头文件中定义 (u)intmax_t 并在标准库的函数中使用它们,它们将成为该库 ABI 的一部分,无论它们是否包含在任何正式的 ABI 规范中。

这对于类 Unix 平台尤其是一个问题,在这些平台上,C 标准库被视为平台的一部分,而不是编译器的一部分。

现在可以转换它了。 Printf 使用宏作为类型说明符,因此可以根据 intmax_t 的大小对这些宏进行不同的定义。宏可以类似地用于将标准库中的少数函数映射到不同的实现,但这是一堆额外的工作以获得可疑的收益,因此 gcc 采取了阻力最小的路径来添加他们所需的功能也就不足为奇了。