Почему в сгенерированном ассемблерном коде std::print большое количество целочисленных констант?

Я играл с «привет, мир!» используя новую функцию std::print в clang 18.1.0, и я заметил пару тысяч строк целочисленных констант, как показано здесь, на godbolt. Какой цели они служат и почему их нет в классическом std::cout «Привет, мир!»?

Различные таблицы Юникода, такие как Extended_grapheme_cluster_table

Jester 14.08.2024 23:10

@Jester плюс, я думаю, в .LJTI17_0 и далее есть несколько таблиц ветвей.

YurkoFlisk 14.08.2024 23:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы имеете в виду такие блоки?

.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) )

Другие вопросы по теме