Я играл с «привет, мир!» используя новую функцию std::print в clang 18.1.0, и я заметил пару тысяч строк целочисленных констант, как показано здесь, на godbolt. Какой цели они служат и почему их нет в классическом std::cout
«Привет, мир!»?
@Jester плюс, я думаю, в .LJTI17_0 и далее есть несколько таблиц ветвей.
Вы имеете в виду такие блоки?
.LJTI17_0:
.long .LBB17_1-.LJTI17_0
.long .LBB17_2-.LJTI17_0
.long .LBB17_24-.LJTI17_0
Это таблицы 32-битных смещений от таблицы до других меток, вероятно, для switch
в позиционно-независимом коде. Часть JTI
в названии ярлыка предположительно означает Jump Table, а I, возможно, означает Indirect?
Я вижу в 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
Их имена кажутся достаточно понятными или, по крайней мере, достаточно длинными, чтобы найти источник кода, который их определяет и использует.
Также таблица степеней 10, умещающаяся в 32 бита, с названием, указывающим назначение:
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
, чтобы получить значение для сравнения и увидеть длину числа в десятичных разрядах, особенно если оно используется неоднократно. И это то, что вы, возможно, захотите сделать, чтобы вы могли сохранять цифры в выходном буфере itoa, когда вы генерируете их сначала в LSD для оснований, отличных от степени 2, и при этом иметь первую цифру в известной позиции. (В противном случае вы могли бы просто начать с конца буфера и скопировать, но это приведет к задержке переадресации в хранилище, если вы сделаете широкую копию, и вы можете не знать, что безопасно писать после конца нечетной длины. См. ️ 🔁 Как напечатать целое число в программировании на уровне ассемблера без printf из библиотеки c (itoa, целое число в десятичную строку ASCII) )
Различные таблицы Юникода, такие как Extended_grapheme_cluster_table