我正在玩“你好世界!”在 clang 18.1.0 上使用新的 std::print 函数进行程序时,我注意到有几千行整数常量,如 here on godbolt 所示。它们的目的是什么?为什么它们没有出现在基于
std::cout
的经典“你好世界!”中?
你是说像这样的块吗?
.LJTI17_0:
.long .LBB17_1-.LJTI17_0
.long .LBB17_2-.LJTI17_0
.long .LBB17_24-.LJTI17_0
这些是从表到其他标签的 32 位偏移量表,可能用于与位置无关的代码中的
switch
。 标签名称的 JTI
部分大概代表跳转表,I 可能代表间接?
我在
parser<char>
中看到有使用它的代码,例如GCC跳转表初始化代码生成movsxd并添加?:
lea rdx, [rip + .LJTI29_0]
movsxd rcx, dword ptr [rdx + 4*rcx] # sign-extending load indexing into the table
add rcx, rdx # add it to the table address
jmp rcx # to get a jump target
Godbolt 默认情况下会过滤库函数的代码,因此代码是隐藏的,但
std::print
是在标头中定义的,因此它会编译为使用它的编译单元的大量代码。与 <iostream>
不同,operator<<
的定义仅在库中,不可内联,因此调用方中的 asm 只是对 std::cout
等全局变量的构造函数调用,并且只是普通的函数调用。
我还看到了一些文字整数常量表,例如
std::__1::__extended_grapheme_custer_property_boundary::__entries:
.long 145
.long 20485
.long 22545
.long 26624
.long 28945
.long 260609
.long 346115
.long 354305
.long 356355
.long 1574642
...
std::__1::__width_estimation_table::__entries:
.long 71303263
.long 147226625
.long 147472385
.long 150618115
.long 150732800
它们的名称似乎相当不言自明,或者至少足够长,可以在源代码中搜索定义和使用它们的代码。
还有一个适合 32 位的 10 的幂表,其名称表明了用途:
std::__1::__itoa::__pow10_32:
.long 0
.long 10
.long 100
.long 1000
.long 10000
.long 100000
.long 1000000
.long 10000000
.long 100000000
.long 1000000000
对小表进行二进制或线性搜索可能比实际乘以
x *= 10
来获取要比较的值以查看数字的十进制数字长度更快,特别是在重复使用的情况下。 这就是您可能想要做的事情,这样您就可以在为非 2 的幂碱基生成 LSD 优先时将数字存储到 itoa 输出缓冲区中,并且仍然在已知位置保留第一个数字。 (否则,您可以从缓冲区的末尾开始并进行复制,但是如果您进行宽复制,则会出现存储转发停顿,并且您可能不知道在奇数长度的末尾写入是否安全。请参阅 如何在汇编级编程中打印整数而不使用 c 库中的 printf(itoa,整数到十进制 ASCII 字符串))