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

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

Код - 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. Также было бы полезно получить доступ к байтам sub-register (в этом случае AL и AH).

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

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

Ответ 1

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

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

Изменить: в ответ на OP, да, вы также можете сделать следующее:

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

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

Ответ 2

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

Добавляя к Крису прекрасный ответ говорит, что ключ использует модификатор между '%' и номером выходного операнда. Например, "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 - напечатать имя регистра QI для указанного операнда.

% b0 будет печатать% al, если операнды [0] - reg 0.

Несколько полезных параметров перечислены в Шаблон вывода GCC Внутренние документы:

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

'% ndigit подобен'% cdigit, за исключением того, что значение константы равно перед печатью отменяется.

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

'% ldigit используется для замены метки_ref в инструкцию перехода.

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

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

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

Обратите внимание на критический, но редко документированный "c" в "%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".

Ответ 3

Пока я об этом думаю... вы должны заменить ограничение "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).

Ответ 4

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

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

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

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

Ответ 5

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

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