2008-09-23 7 views
11

Кто-нибудь знает, как я могу избавиться от следующего предупреждения ассемблера?GCC inline ассемблер, размеры регистра микшера (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-код использовать определенный регистр (и его суборегистры), но я хочу оставить задание распределения регистров до компилятора.

ответ

18

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

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

Edit: В ответ на OP, да, вы можете сделать следующее тоже:

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

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

+0

Я также тестировал .. Знаете ли вы также и модификаторы для низких и высоких байтов? –

+0

Спасибо, я рад, что это помогло! –

+1

'xchg% al,% ah' - это 3 процессора на процессорах Intel, а чтение 16-битного топора приводит к тому, что на некоторых процессорах происходит свалка с частичным регистром или дополнительные удары. 'ror $ 8,% ax' - 1 uop, поэтому это определенно предпочтительнее. Кроме того, модификаторы операндов [теперь задокументированы в руководстве] (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86Operandmodifiers) (используя этот же пример, возможно, не совпадение: P). См. Также: модификаторы операнда для векторных рег: http://stackoverflow.com/questions/34459803/in-gnu-c-inline-asm-whatre-the-modifiers-for-xmm-ymm-zmm-for-a-single -operand –

0

Так что, видимо, есть трюки, чтобы сделать это ... но это может быть не так эффективно. 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 этой оптимизируется до шести инструкций ...

+0

Как 6 инструкций должны быть быстрее, чем 1 инструкция ?! Мои тесты времени (для миллиарда прогонов, 5 испытаний): my version = (4.38, 4.48, 5.03, 4.10, 4.18), ваша версия = (5.33, 6.21, 5.62, 5.32, 5.29). –

+0

Итак, мы смотрим на улучшение скорости на 20%. Разве это не «намного быстрее»? –

+1

Крис, абсолютно прав ... ваша версия * есть * быстрее похоже. Но не так много, как 6-инструкции-против-1-инструкции приведут вас к ожиданию, и об этом я и предупреждал. Я действительно не делал сравнения самостоятельно, поэтому реквизит для вас для тестирования! –

0

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

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

+0

@Dan, Дважды я проверил документацию по GCC, а затем отправил отчет об ошибке, потому что эта информация отсутствует. Кто знает - возможно, она попадает в следующую версию. –

+1

Я нашел ошибку на http://gcc.gnu.org/bugzilla/ show_bug.cgi? id = 37621, и похоже, что может быть сопротивление документированию этой функции, поскольку оно предназначено только для внутреннего использования. Hrm ... –

1

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

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

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

+0

Да, хорошо, спасибо! ост сейчас. :-) –

8

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

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

Несколько более полезные варианты перечислены в Output Template в GCC Internals документации:

'% 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» (который, кажется, для ПОС)

#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».

+2

«Полный» стол теперь находится в этом исходном файле, как и комментарий перед функцией 'ix86_print_operand()'. Он также упоминает (среди прочих) '% p..' /'% P..'. –