2014-01-20 3 views
7

Я пытаюсь отключить/включить кеш в пространстве ядра Linux.C использует сборку: несоответствие типа операнда для push

код я использую

__asm__ __volatile__(
     "pushw %eax\n\t" /*line 646*/ 
     "movl %cr0,%eax\n\t" 
     "orl $0x40000000,%eax\n\t" 
     "movl %eax,%cr0\n\t" 
     "wbinvd\n\t" 
     "pop %eax"); 

После компилировать, я получил сообщение об ошибке следующим образом:

memory.c: Assembler messages: 
memory.c:645: Error: operand type mismatch for `push' 
memory.c:646: Error: unsupported for `mov' 
memory.c:648: Error: unsupported for `mov' 
memory.c:650: Error: operand type mismatch for `pop' 
make[4]: *** [memory.o] Error 1 

Моя машина Intel (R) Xeon (R) CPU E5-1650 v2 @ 3,50 ГГц. 64-битная машина.

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

Я предполагаю, что это из-за несоответствия инструкции и регистра. Но я смущен тем, как это исправить. :(

Заранее спасибо

+1

не 'pushw' для размера слова (16 бит)? eax 32bit, попробуйте 'pushl' – Leeor

+0

Привет @Leeor, Большое спасибо за ваш комментарий! но pushl сообщит об ошибке: memory.c: 645: Ошибка: недопустимый суффикс инструкции для 'push '; Я также пробовал pushq, который не работает ни – Mike

ответ

1

По разведданные - http://download.intel.com/products/processor/manual/325383.pdf слова 16 бит так pushw ожидает 16 битного операнд регистра EAX 32 бита и должен быть толкал с помощью PushL Edit!..: сборки вы для 32 бит или 64 бит?

+0

моя машина 64bit, поэтому я думаю, что eax - это 64-битный регистр? – Mike

+1

Это может быть полезно - http://www.x86-64.org/documentation/assembly.html В нем рассказывается о инструкциях push в параграфе 5. –

+0

на длинах регистра x86 никогда не изменяется. При изменении размера они используют новое имя/префикс, поэтому вы можете адресовать все типы регистров от байта до 8 байтов. Прежде чем писать сборку, вы должны прочитать об архитектуре. –

9

Хотя большинство регистров 32bit сохраняются в 64-битные архитектуры, они больше не способны взаимодействовать со стеком. Поэтому, пытаясь толкать или поп-%eax операция является недопустимой. Так что если вы хотите играть со стеком, вы должны использовать %rax, что эквивалентно 64-битной архитектуре %eax.

+0

Намного лучше: используйте кубик вместо сохранения/восстановления. –

1

Если вы не догадались, использовать pushq %rax если компиляции 64 бит

2

Правильный подход заключается в объявить затирать на %eax, вместо сохранения/восстановления сами. Компилятор, вероятно, может сделать что-то более эффективное, чем push/pop, например, использовать разные регистры для любых значений, которые он хочет оставаться вживую. Это также означает, что вам не нужен другой код для 64 бит для сохранения/восстановления %rax.

Обратите внимание, что pushq %rax/popq %rax бы не быть безопасным в пользовательском пространстве кода на x86-64. Там no way to tell gcc that inline-asm clobbers the red-zone. Это было бы безопасно в коде ядра, где ABI не использует красную зону, но опять же, он все еще побеждает цель синтаксиса inline asm GNU C.


Там есть дополнительные морщины здесь: mov %cr0, %eaxisn't a valid 64bit instruction. Вы должны использовать 64-битный регистр.

Позволяя компилятору выбрать реестр для нас, эта проблема решается и дает компилятору больше свободы, поэтому в любом случае это лучше. Объявите переменную C с 64-битным типом в ABI x86-64 и 32-битным в i386 ABI. (например, long, так как это для ядра Linux ABI, а не для Windows, где long всегда 32 бит. uintptr_t - еще один вариант, который будет работать в ядре Linux. (Но не в пользовательском пространстве: x32 - длинный режим с 32-битными указателями).)

// is this enable or disable? I didn't check the manual 
void set_caching_x86(void) { 
    long tmp;  // mov to/from cr requires a 64bit reg in 64bit mode 
    asm volatile(
     "mov %%cr0, %[tmp]\n\t"  // Note the double-% when we want a literal % in the asm output 
     "or $0x40000000, %[tmp]\n\t" 
     "mov %[tmp], %%cr0\n\t" 
     "wbinvd\n\t" 
     : [tmp] "=r" (tmp) // outputs 
     : // no inputs 
     : // no clobbers. "memory" clobber isn't needed, this just affects performance, not contents 
    ); 
} 

Это compiles and assembles to what we want, с или без -m32, как вы можете видеть на Godbolt Compiler Explorer.

При записи вручную легче использовать размер операнда операндами, а не всегда использовать суффикс на мнемонике. т. е. push %eax работал бы (в 32-битном режиме), но все же был хуже, чем позволить компилятору позаботиться об этом.

Мы могли бы использовать %k[tmp], чтобы получить %eax (или что-то еще) даже в режиме 64 бит, но это должно было бы обнулить верхние 32b. Расход 1 байт на префикс REX для инструкции or стоит того, чтобы быть более надежным для будущих процессоров, которые могут заботиться о том, что вы пишете на верхнем 32b регистра управления.

volatile гарантирует, что оператор asm не оптимизирован, даже если выходное значение никогда не используется.

4

Есть несколько проблем с вашей встроенной операцией сборки, большинство из которых указаны сообщениями об ошибках.

Первое сообщение об ошибке Error: operand type mismatch for `push', соответствует инструкции pushw %eax. Ошибка возникает из-за того, что суффикс размера операнда, который вы использовали, w, не соответствует фактическому размеру операнда, %eax. Вы сказали ему использовать инструкцию для нажатия 16-битного значения в стеке, но при условии, что 32-разрядный регистр является операндом. Вы можете исправить это, используя pushw %ax, но это не то, что вы хотите. Он сохранил бы только младшие 16 бит регистра RAX, а не весь регистр.

Другим «очевидным» решением было бы использовать pushl %eax, но есть две проблемы с этим. Сначала, чтобы исправить другие проблемы, вам необходимо изменить весь регистр RAX, а это значит, что вам нужно сохранить все 64 бита, а не только более низкие 32 бита. Во-вторых, нет 32-разрядной команды PUSH в 64-битном режиме, поэтому вы вынуждены использовать pushq %rax независимо.

Следующие два сообщения об ошибке: Error: unsupported for `mov'. Эти сообщения об ошибках соответствуют инструкциям movl %cr0,%eax и movl %eax,%cr0. и оба являются результатом одной и той же проблемы. В 64-битном режиме нет 32-разрядной версии этих операндов. Вам нужно использовать 64-битный операнд, поэтому исправить просто использовать RAX вместо EAX. Вот где все 64-битные RAX сбиваются, и почему я сказал, что вам нужно сохранить весь регистр.

Последнее сообщение об ошибке Error: operand type mismatch for `pop'. Это результат аналогичной проблемы, такой как первая. В этом случае вы не использовали суффикс размера операнда, а это значит, что ассемблер попытается определить размер операнда на основе операндов. Поскольку вы использовали 32-разрядный операнд, %eax, он использует 32-разрядный размер операнда. Как и в случае с PUSH, в 64-битном режиме есть 32-разрядная команда POP, поэтому вы также не можете использовать %eax. В любом случае, поскольку инструкция PUSH должна быть 64-разрядной, для команды POP должно быть установлено 64-битное соответствие, поэтому исправление должно использовать popq %rax.

Наконец, одна из проблем, которая не указана сообщением об ошибке, заключается в том, что в 64-битном режиме размер CR0 расширяется до 64 бит. В то время как дополнительные 32 бита в настоящее время зарезервированы и должны быть установлены на ноль, они могут быть определены в будущих процессорах. Поэтому инструкция orl $0x40000000,%eax должна сохранять верхние 64-битные. К сожалению, этого не происходит, он очистит верхние 32-битные биты RAX, что означает, что эта инструкция также непреднамеренно очистит любой из этих битов, которые могут дать будущие процессоры. Поэтому его следует заменить на orq $0x40000000,%rax.

Таким образом, фиксированная последовательность команд будет:

pushq %rax 
    movq %cr0, %rax 
    orq  $0x40000000, %rax 
    movq %rax, %cr0 
    wbinvd 
    popq %rax 

Это не то, что я собираюсь предложить использовать в вашей инлайн сборки, однако. Это можно упростить, позволяя GCC выбрать используемый регистр. Таким образом, нет необходимости его сохранять. Вот что я хотел бы предложить вместо этого:

long long dummy; 
asm volatile ("movq %%cr0, %0\n\t" 
       "orq $0x40000000, %0\n\t" 
       "movq %0, %%cr0\n\t" 
       "wbinvd" 
       : "=r" (dummy) : :); 
+0

'long long' является 64-битным даже в 32-битном режиме. Я думал, что это не сработает, но, по-видимому, это так. С '-m32' компилятор, вероятно, резервирует для него два регистра, но'% 0' только расширяется до одного из них? Возможно, он выбирает «A»: «edx: eax»? В моем ответе я предложил использовать 'long'. (Я использовал 'tmp', пока не увидел, что ваш ответ был опубликован в течение моих минут, указав, что x86-64 не может переместить cr в 32-битный регистр.) –

+0

@PeterCordes Я не пытаюсь написать что-то, что будет работать в 64-битных режимах. И, я, я уже исправил эту проблему ORL, я случайно заметил ее перед тем, как вы обновили свой пост. –

Смежные вопросы