Скопируйте байт в другой регистр в GNU C inline asm, где компилятор выбирает регистры для обоих операндов - PullRequest
0 голосов
/ 18 февраля 2020

Я пытаюсь поиграться со строками во встроенном asm для c. Я смог понять, как работает strcpy (показано ниже):

static inline char *strcpy(char *dest, char *src)
{
  int d0, d1, d2;
  char temp;

  asm volatile(
    "loop:  lodsb;"   /* load value pointed to by %si into %al, increment %si */
    "       stosb;"   /* move %al to address pointed to by %di, increment %di */
    "       testb %%al, %%al;"
    "       jne loop;"
    : "=&S" (d0), "=&D" (d1), "=&a" (d2)
    : "0" (src), "1" (dest)
    : "memory"
  );
}

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

static inline char *strcpy(char *dest, char *src)
{
  int d0, d1, d2;
  char temp;

  asm volatile(
    "loop:  lodsb;"   /* load value pointed to by %si into %al, increment %si */
    "       mov %2, %3;" /* move al into temp */
    /*
     *
     * Do and comparisons and jumps based off how I want to change the characters
     *
     */
    "       stosb;"   /* move %al to address pointed to by %di, increment %di */
    "       testb %%al, %%al;"
    "       jne loop;"
    : "=&S" (d0), "=&D" (d1), "=&a" (d2), "+r" (temp)
    : "0" (src), "1" (dest)
    : "memory"
  );
}

Где я в основном перемещаю байт, помещенный в %al с помощью инструкции lodsb, во временную переменную, где я делаю любую обработку после , Тем не менее, кажется, что персонаж никогда не сохраняется в temp по какой-то причине, которую я не могу понять.

1 Ответ

2 голосов
/ 18 февраля 2020

Ваша вторая версия даже не будет собираться, потому что temp и d2 имеют разные размеры, поэтому вы получите mov %eax, %dl из G CC: https://godbolt.org/z/tng4g4. Когда встроенный asm не делает то, что вам нужно, всегда смотрите на генерируемый компилятором asm , чтобы увидеть, что компилятор фактически подставил в ваш шаблон (и какие регистры он выбрал для какого операнда).

Это не соответствует тому, что вы описываете (работает, но не работает), поэтому это не MCVE того, что вы делали. Но реальный вопрос по-прежнему отвечает.

Один простой способ - объявить оба C временных значения одинакового размера, чтобы G CC выбирал одинаковые регистры ширины.

Или вы можете использовать переопределения размера, такие как mov %k2, %k3, чтобы получить movl или mov %b2, %b3, чтобы получить movb (8-битный размер операнда).

Странно, вы выбрали int для "=a" временно, поэтому компилятор выбирает EAX, даже если вы загружаете только char.

Я бы порекомендовал movzbl %2b, %3k использовать размеры, противоположные тому, как вы объявили переменные; это более эффективно, чем объединение байта с младшим байтом адресата, и позволяет избежать (или добавить еще) неполных регистров в процессорах семейства P6, ранних семейств Sandybridge, и , которые не работают любое частичное переименование регистра. Кроме того, Intel, так как Ivybridge может выполнять удаление с его помощью.


Кстати, ваша первая версия strcpy выглядит безопасной и правильной, приятно. И да, "memory" clobber необходим.

Err, по крайней мере, встроенный асм правильный. У вас есть C неопределенное поведение от выпадения конца не-1038 * функции без оператора return.

Вы можете упростить операнды asm с помощью "+&S"(src) операнд чтения / записи вместо фиктивного вывода, потому что вы находитесь внутри функции-обертки (поэтому можно изменить локальную src этой функции). Фиктивный вывод с ограничением совпадения - это канонический способ получения ввода в регистр, который вы хотите уничтожить.

(Если вы хотите работать как плохо спроектированный ISO C strcpy, вы бы хотели char *retval = dst перед оператором asm, если вы собираетесь использовать приведенное выше предложение операндов "+S" и "+D". Лучше было бы назвать его stpcpy и вернуть указатель на конец пункта назначения. Кроме того, ваш sr c должен быть const char*.)

Конечно, использовать lodsb / stosb не особенно эффективно в al oop, особенно на процессорах, которые не переименовывают AL отдельно от RAX, поэтому для каждой загрузки также требуется ALU uop для слияния с RAX. Но побайтовое время намного хуже, чем вы можете сделать с SSE2, поэтому оптимизация этого с нагрузками movzx, и, возможно, индексированный режим адресации, вероятно, не стоит проблем. См. https://agner.org/optimize/ и другие ссылки для оптимизации в { ссылка }, особенно https://uops.info/, для информации о задержке / пропускной способности / числе операций. (stosb равно 3 мопам против 2 для магазина mov + inc edi.)

Если вы на самом деле оптимизируете размер кода по скорости, просто используйте 8-бит или 32- бит mov для копирования регистров, а не movzbl.


Кстати, с таким количеством операндов вы, вероятно, захотите использовать именованные операнды, такие как [src] "+&S"(src) в ограничениях, а затем %[src] в шаблон.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...