какие инструкции MOV в x86 не используются или используются реже всего и могут использоваться для собственного расширения MOV - PullRequest
1 голос
/ 18 марта 2020

Я моделирую пользовательскую инструкцию MOV в архитектуре X86 в симуляторе gem5, чтобы проверить ее реализацию на симуляторе, мне нужно скомпилировать мой код C, используя встроенную сборку для создания двоичного файла. Но так как это пользовательская инструкция, которая не была реализована в компиляторе G CC, компилятор выдаст ошибку. Я знаю, что одним из способов является расширение компилятора G CC для принятия моей пользовательской инструкции X86, но я не хочу делать это, так как это занимает больше времени (но потом это будет сделано).

Как временный взлом (просто чтобы проверить, стоит ли моя реализация или нет). Я хочу отредактировать уже MOV-инструкцию, одновременно изменяя ее базовые «микрооперации» в симуляторе, чтобы обмануть G CC, чтобы принять мою «пользовательскую» инструкцию и скомпилировать.

Так как они представляют собой много типов команд MOV, которые доступны в архитектуре x86. Поскольку они представляют собой различные инструкции MOV в архитектуре 86 , ссылка .

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

1 Ответ

3 голосов
/ 18 марта 2020

Ваша лучшая ставка - обычная mov с префиксом, который G CC никогда не выдаст самостоятельно. т.е. создайте новую кодировку mov, которая включает обязательный префикс перед любым другим mov.

. Или, если вы изменяете G CC и as, вы можете добавить новый мнемони c, который просто использует недопустимые (в 64-битном режиме) однобайтовые коды операций для версий mov источника памяти, памяти и непосредственного источника. AMD64 освободил несколько кодов операций, включая инструкции BCD, такие как AAM, и большинство регистров сегмента push / pop. (Вы все еще можете mov в / из Sregs, но они не тратят впустую 1 код операции на Sreg.)

Предполагая, что моя рабочая нагрузка включает только целые числа, то есть, скорее всего, я не буду использовать регистры xmm и mmx

Плохое предположение для XMM: G CC агрессивно использует 16-байтовый movaps / movups вместо копирования структур 4 или 8 байтов за раз. Нередко встречаются векторные команды mov в скалярном целочисленном коде как часть встроенного расширения небольшой известной длины memcpy или структуры / массива init. Кроме того, эти mov инструкции имеют как минимум 2-байтовые коды операций (SSE1 0F 28 movaps, поэтому префикс перед простым mov имеет тот же размер, что и ваша идея).

Однако вы правы в отношении MMX regs. Я не думаю, что современный G CC когда-либо будет излучать movq mm0, mm1 или вообще использовать MMX, если вы не используете встроенные MMX. Определенно нет при нацеливании на 64-битный код.

Также mov в / из управляющих регистров (0f 21/23 /r) или регистров отладки (0f 20/22 /r) оба mov мнемони c, но g cc определенно никогда не будут излучать ни сами по себе. Доступно только с операндами регистра GP в качестве операнда, который не является регистром отладки или управления. Так что это технически ответ на ваш заглавный вопрос, но, вероятно, не тот, который вы на самом деле хотите.


G CC не анализирует строку встроенного шаблона asm, он просто включает ее в вывод текста asm передать ассемблеру после замены %number операндов. Таким образом, сам G CC не является препятствием для выдачи произвольного asm-текста с использованием встроенного asm.

И вы можете использовать .byte для вывода произвольного машинного кода.

Возможно, хорошим вариантом будет использовать 0E байт в качестве префикса для вашей специальной кодировки mov, которую вы собираетесь специально декодировать в GEM. 0E равно push CS в 32-битном режиме , недопустимо в 64-битном режиме. G CC никогда не будет излучать.

Или просто префикс F2 repne; G CC никогда не выдаст repne перед кодом операции mov (если он не применяется), только movs. (F3 rep / repe означает xrelease при использовании в инструкции назначения памяти, поэтому не используйте его. https://www.felixcloutier.com/x86/xacquire: xrelease говорит, что F2 repne - это префикс xacquire при использовании с lock ed инструкции, которые не включают mov в память, поэтому там будут игнорироваться.)

Как обычно, префиксы, которые не применяются, не имеют документированного поведения, но на практике процессоры, которые не rep / repne не может игнорировать это. Некоторые будущие процессоры могут понимать, что это означает что-то особенное, и это именно то, что вы делаете с GEM.

Выбор .byte 0x0e; вместо repne; может быть лучшим выбором, если вы хотите guard против случайного оставления этих префиксов в сборке, которую вы запускаете на реальном процессоре . (Это будет #UD -> SIGILL в 64-битном режиме или, как правило, * * * * * sh не испортит стек в 32-битном режиме.) Но если вы do хотите иметь возможность запустить точный тот же двоичный файл на реальном процессоре, с тем же выравниванием кода и всем остальным, тогда игнорируемый префикс REP идеален.


Преимущество использования префикса перед стандартной mov инструкцией позволить ассемблеру кодировать для вас операнды:

template<class T>
void fancymov(T& dst, T src) {
    // fixme: imm -> mem  needs a size suffix, defeating template
    // unless you use Intel-syntax where the operand includes "dword ptr"
    asm("repne; movl  %1, %0"
#if 1
       : "=m"(dst)
       : "ri" (src)
#else
       : "=g,r"(dst)
       : "ri,rmi" (src)
#endif
       : // no clobbers
    );
}

void test(int *dst, long src) {
    fancymov(*dst, (int)src);
    fancymov(dst[1], 123);
}

(Много альтернативные ограничения позволяют компилятору выбирать либо назначение reg / mem, либо источник reg / mem. На практике он предпочитает назначение регистра даже когда это будет стоить другой инструкции, чтобы сделать свой собственный магазин, так что это отстой.)

В проводнике компилятора Godbolt , для версии, которая позволяет только назначение памяти:

test(int*, long):
        repne; movl  %esi, (%rdi)       # F2 E9 37
        repne; movl  $123, 4(%rdi)      # F2 C7 47 04 7B 00 00 00
        ret

Если вы хотите, чтобы это можно было использовать для нагрузок, я думаю, вы бы необходимо сделать 2 отдельные версии функции и вручную использовать загруженную версию или версию хранилища, где это необходимо, поскольку G CC, кажется, хочет использовать reg, reg всякий раз, когда это возможно.


Или с версия, разрешающая регистровые выходы (или другая версия, которая возвращает результат в виде T, см. ссылку Godbolt):

test2(int*, long):
        repne; mov  %esi, %esi
        repne; mov  $123, %eax
        movl    %esi, (%rdi)
        movl    %eax, 4(%rdi)
        ret
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...