什么 ABI(如果有的话)限制 [u]intmax_t 的大小?
What ABI, if any, restricts the size of [u]intmax_t?
从 1999 版开始,ISO C 标准定义了一个标准 header <stdint.h>
,其中定义了 typedef intmax_t
和 uintmax_t
。这些分别指定 "a (signed|unsigned) integer type capable of representing any value of any (signed|unsigned) integer type".
例如,如果通常情况下最宽的有符号和无符号整数类型是 long long int
和 unsigned long long int
,它们通常都是 64 位,那么 intmax_t
和 uintmax_t
可以在 <stdint.h>
中定义如下:
typedef long long int intmax_t;
typedef unsigned long long int uintmax_t;
有一组有限的预定义有符号和无符号整数类型,范围从 signed
、unsigned
和普通 char
到 signed
和 unsigned
long long int
.
C99 和 C11 还允许实现定义 扩展整数类型 ,它们不同于任何标准类型,并且具有 implementation-defined 关键字的名称。
gcc 和 clang 在一些但不是所有目标上都支持类型 __int128
和 unsigned __int128
。它们的行为类似于 128 位整数类型,但它们 不 被视为 扩展整数类型 ,并且两个编译器的文档都指出它们不支持任何扩展整数类型。因为这些不是标准定义术语的 整数类型 ,所以 typedef intmax_t
和 uintmax_t
适用于 64 位类型,而不是 128 位类型。
None 这违反了 C 标准(实现不需要任何扩展整数类型,并且只要它们不破坏任何严格符合的程序,它们就可以具有任意扩展) .但在我看来,将 __int128
和 unsigned __int128
视为扩展整数类型以及将 intmax_t
和 uintmax_t
视为 128 位类型是完全合理的.
不这样做的理由是改变intmax_t
和uintmax_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_t
或uintmax_t
,甚至<stdint.h>
header。它确实指定了预定义整数类型的大小和对齐方式(在图 3.1 中)。
最后,我的问题是:intmax_t
和 uintmax_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
作为 扩展整数类型 .
参考文献:
- N1256,C99标准草案
- N1570,C11标准草案
- N2303,Jens Gustedt 的提案
- System V AMD64 ABI
更改 intmax_t
和 uintmax_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 标准库进行匹配更改。具体来说,它至少需要更新 printf
和 scanf
函数系列、imaxabs
、imaxdiv
、strtoimax
和 strtoumax
以及它们的变体。
我认为这里要理解的关键是,ABI 规范中没有记录某些内容并不意味着它不是 ABI 的一部分。一旦跨库边界使用类型,它的属性就会成为该库 ABI 的一部分。
通过在标准头文件中定义 (u)intmax_t 并在标准库的函数中使用它们,它们将成为该库 ABI 的一部分,无论它们是否包含在任何正式的 ABI 规范中。
这对于类 Unix 平台尤其是一个问题,在这些平台上,C 标准库被视为平台的一部分,而不是编译器的一部分。
现在可以转换它了。 Printf 使用宏作为类型说明符,因此可以根据 intmax_t 的大小对这些宏进行不同的定义。宏可以类似地用于将标准库中的少数函数映射到不同的实现,但这是一堆额外的工作以获得可疑的收益,因此 gcc 采取了阻力最小的路径来添加他们所需的功能也就不足为奇了。
从 1999 版开始,ISO C 标准定义了一个标准 header <stdint.h>
,其中定义了 typedef intmax_t
和 uintmax_t
。这些分别指定 "a (signed|unsigned) integer type capable of representing any value of any (signed|unsigned) integer type".
例如,如果通常情况下最宽的有符号和无符号整数类型是 long long int
和 unsigned long long int
,它们通常都是 64 位,那么 intmax_t
和 uintmax_t
可以在 <stdint.h>
中定义如下:
typedef long long int intmax_t;
typedef unsigned long long int uintmax_t;
有一组有限的预定义有符号和无符号整数类型,范围从 signed
、unsigned
和普通 char
到 signed
和 unsigned
long long int
.
C99 和 C11 还允许实现定义 扩展整数类型 ,它们不同于任何标准类型,并且具有 implementation-defined 关键字的名称。
gcc 和 clang 在一些但不是所有目标上都支持类型 __int128
和 unsigned __int128
。它们的行为类似于 128 位整数类型,但它们 不 被视为 扩展整数类型 ,并且两个编译器的文档都指出它们不支持任何扩展整数类型。因为这些不是标准定义术语的 整数类型 ,所以 typedef intmax_t
和 uintmax_t
适用于 64 位类型,而不是 128 位类型。
None 这违反了 C 标准(实现不需要任何扩展整数类型,并且只要它们不破坏任何严格符合的程序,它们就可以具有任意扩展) .但在我看来,将 __int128
和 unsigned __int128
视为扩展整数类型以及将 intmax_t
和 uintmax_t
视为 128 位类型是完全合理的.
不这样做的理由是改变intmax_t
和uintmax_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 changingintmax_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_t
或uintmax_t
,甚至<stdint.h>
header。它确实指定了预定义整数类型的大小和对齐方式(在图 3.1 中)。
最后,我的问题是:intmax_t
和 uintmax_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
作为 扩展整数类型 .
参考文献:
- N1256,C99标准草案
- N1570,C11标准草案
- N2303,Jens Gustedt 的提案
- System V AMD64 ABI
更改 intmax_t
和 uintmax_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 标准库进行匹配更改。具体来说,它至少需要更新 printf
和 scanf
函数系列、imaxabs
、imaxdiv
、strtoimax
和 strtoumax
以及它们的变体。
我认为这里要理解的关键是,ABI 规范中没有记录某些内容并不意味着它不是 ABI 的一部分。一旦跨库边界使用类型,它的属性就会成为该库 ABI 的一部分。
通过在标准头文件中定义 (u)intmax_t 并在标准库的函数中使用它们,它们将成为该库 ABI 的一部分,无论它们是否包含在任何正式的 ABI 规范中。
这对于类 Unix 平台尤其是一个问题,在这些平台上,C 标准库被视为平台的一部分,而不是编译器的一部分。
现在可以转换它了。 Printf 使用宏作为类型说明符,因此可以根据 intmax_t 的大小对这些宏进行不同的定义。宏可以类似地用于将标准库中的少数函数映射到不同的实现,但这是一堆额外的工作以获得可疑的收益,因此 gcc 采取了阻力最小的路径来添加他们所需的功能也就不足为奇了。