Встроенный ассемблер GCC, смешивающий размеры регистров (x86)

StackOverflow https://stackoverflow.com/questions/118730

  •  02-07-2019
  •  | 
  •  

Вопрос

Кто-нибудь знает, как я могу избавиться от следующего предупреждения ассемблера?

Код x86, 32-разрядный:

int test (int x)
{
  int y;
  // do a bit-rotate by 8 on the lower word. leave upper word intact.
  asm ("rorw $8, %0\n\t": "=q"(y) :"0"(x));
  return y;
}

Если я скомпилирую его, я получу следующее (очень достоверное) предупреждение:

Warning: using `%ax' instead of `%eax' due to `w' suffix

То, что я ищу, - это способ сообщить компилятору / ассемблеру, что я хочу получить доступ к младшему 16-разрядному вспомогательному регистру %0.Также было бы неплохо знать доступ к байтовым подрегистрам (в данном случае AL и AH).

Я уже выбрал модификатор "q", поэтому компилятор вынужден использовать EAX, EBX, ECX или EDX.Я позаботился о том, чтобы компилятор выбрал регистр, у которого есть подрегистры.

Я знаю, что могу заставить asm-код использовать определенный регистр (и его подрегистры), но я хочу оставить задание по распределению регистров на усмотрение компилятора.

Это было полезно?

Решение

Вы можете использовать %w0 если я правильно помню.Я тоже только что это проверил.:-)

int
test(int x)
{
    int y;
    asm ("rorw $8, %w0" : "=q" (y) : "0" (x));
    return y;
}

Редактировать:В ответ на ОП, да, вы также можете сделать следующее:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

В настоящее время единственное место (о котором я знаю), где это задокументировано, - это gcc/config/i386/i386.md, отсутствует ни в одной стандартной документации.

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

Давно, но мне, скорее всего, это понадобится для моего собственного использования в будущем...

В дополнение к прекрасному ответу Криса говорится, что ключ использует модификатор между '%' и номером выходного операнда.Например, "MOV %1, %0" может стать "MOV %q1, %w0".

Я ничего не смог найти в constraints.md, но /gcc/config/i386/i386.c был ли этот потенциально полезный комментарий в источнике для print_reg():

/* Print the name of register X to FILE based on its machine mode and number.
   If CODE is 'w', pretend the mode is HImode.
   If CODE is 'b', pretend the mode is QImode.
   If CODE is 'k', pretend the mode is SImode.
   If CODE is 'q', pretend the mode is DImode.
   If CODE is 'x', pretend the mode is V4SFmode.
   If CODE is 't', pretend the mode is V8SFmode.
   If CODE is 'h', pretend the reg is the 'high' byte register.
   If CODE is 'y', print "st(0)" instead of "st", if the reg is stack op.
   If CODE is 'd', duplicate the operand for AVX instruction.
 */

Комментарий ниже для ix86_print_operand() приведите пример:

b - выведите имя QImode регистра для указанного операнда.

%b0 напечатал бы %al, если операнды [0] имеют значение reg 0.

Еще несколько полезных опций перечислены в разделе Выходной шаблон из числа Внутренние компоненты GCC Документация:

‘%cdigit’ может использоваться для замены операнда, который является постоянным значением без синтаксиса, который обычно указывает на непосредственный операнд.

‘%ndigit’ похож на ‘%cdigit’, за исключением того, что значение константы отрицается перед печатью.

‘%adigit’ может использоваться для замены операнда, как если бы это была ссылка на память , при этом фактический операнд обрабатывается как адрес.Это может быть полезно при выводе инструкции “адрес загрузки”, потому что часто синтаксис ассемблера для такой инструкции требует, чтобы вы записывали операнд, как если бы это была ссылка на память.

‘%ldigit’ используется для замены label_ref в команду перехода.

‘%=’ выводит число, которое является уникальным для каждой инструкции в полный сборник.Это полезно для создания местного надписи упоминается чаще, чем раз в один и тот же шаблон, который генерирует несколько инструкций ассемблера.

Тот самый '%c2' конструкция позволяет правильно отформатировать инструкцию LEA, используя смещение:

#define ASM_LEA_ADD_BYTES(ptr, bytes)                            \
    __asm volatile("lea %c1(%0), %0" :                           \
                   /* reads/writes %0 */  "+r" (ptr) :           \
                   /* reads */ "i" (bytes));

Обратите внимание на важную, но малодокументированную букву "с" в '%c1'.Этот макрос эквивалентен

ptr = (char *)ptr + bytes

но без использования обычных портов выполнения целочисленной арифметики.

Отредактируйте, чтобы добавить:

Выполнение прямых вызовов в x64 может быть затруднено, поскольку для этого требуется еще один недокументированный модификатор:'%P0' (который, кажется, предназначен для PIC)

#define ASM_CALL_FUNC(func)                                         \
    __asm volatile("call %P0") :                                    \
              /* no writes */ :                                     \
              /* reads %0 */ "i" (func))                           

Модификатор "p" в нижнем регистре также, по-видимому, функционирует одинаково в GCC, хотя ICC распознает только заглавную букву "P".Более подробная информация, вероятно, доступна по адресу /gcc/config/i386/i386.c.Найдите "'p'".

Пока я думаю об этом ...вы должны заменить ограничение "q" на ограничение с заглавной буквы "Q" во втором решении Криса:

int
test(int x)
{
    int y;
    asm ("xchg %b0, %h0" : "=Q" (y) : "0" (x));
    return y;
}

"q" и "Q" немного отличаются в 64-разрядном режиме, где вы можете получить младший байт для всех регистров целых чисел (ax, bx, cx, dx, si, di, sp, bp, r8-r15).Но вы можете получить только второй по старшинству байт (напримерah) для четырех исходных регистров 386 (ax, bx, cx, dx).

Так что, очевидно, для этого есть какие-то хитрости...но это может быть не так эффективно.32-разрядные процессоры x86, как правило, медленно при манипулировании 16-битными данными в регистрах общего назначения.Вы должны сравнить его, если важна производительность.

Если это (а) не критично для производительности и (б) не окажется намного быстрее, я бы избавил себя от некоторых хлопот с обслуживанием и просто сделал это на C:

uint32_t y, hi=(x&~0xffff), lo=(x&0xffff);
y = hi + (((lo >> 8) + (lo << 8))&0xffff);

С GCC 4.2 и -O2 это оптимизируется до шести инструкций...

Попался.Что ж, если это примитивная процедура, которую вы собираетесь использовать снова и снова, я с этим не спорю...трюк с именованием регистра, на который указал Крис, очень хорош, и мне придется его запомнить.

Было бы неплохо, если бы это тоже вошло в стандартные документы GCC!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top