встроенный ассемблер неизвестен - PullRequest
3 голосов
/ 20 марта 2012
static inline void *__memset(void *s, char c, size_t n) {
int d0, d1;
asm volatile (
    "rep; stosb;"
    : "=&c" (d0), "=&D" (d1)
    : "0" (n), "a" (c), "1" (s)
    : "memory");
return s;
}

Для чего используются "d0" и "d1"? Не могли бы вы объяснить весь код полностью? Спасибо!

Ответы [ 2 ]

4 голосов
/ 20 марта 2012

Вам необходимо понимать расширенный встроенный asm-формат gcc:

  • Первая часть - это фактическая сборка. В этом случае есть только 2 инструкции
  • Вторая часть определяет выходные ограничения, а третья часть определяет входные ограничения. Четвертая часть указывает, что сборка будет заглатывать память

выход

  • "=&c" связывает d0 с регистром ecx и отмечает его только для записи. & означает, что его можно изменить до конца кода
  • "=&D" означает то же самое для регистра edi

Input

  • "0" (n) ассоциируется с первым упомянутым регистром. В вашем случае с ecx
  • "a" (c) ассоциирует c с eax
  • "1" (s) ассоциируется с edi

Монтаж

Итак, вот оно. Повторите это время ecx (n раз): сохраните eax (c) в edi (s), затем увеличьте его.


Итак, почему неиспользованные d0 и d1? Я не уверен. Я тоже думаю, что они бесполезны в этом случае, и весь раздел вывода можно оставить пустым, НО я не думаю, что можно указать «доступный для записи» и « early -lobbered» во входных ограничениях. Поэтому я думаю, d0 и d1 могут сделать возможным &.

Я бы попробовал написать это так:

asm volatile (
    "rep\n"
    "stosb\n"
    :
    : "c" (n), "a" (c), "D" (s)
    : "%ecx", "%edi", "memory"
);
1 голос
/ 20 марта 2012

Для чего используются "d0" и "d1"?

По сути, это говорит о том, что конечные значения %ecx, %edi (при условии 32-разрядного)хранятся в d0, d1 соответственно.Это служит нескольким целям:

Это позволяет компилятору знать, что в качестве выходных данных эти регистры эффективно перекрываются.Присваивая их временным переменным, оптимизирующий компилятор также знает, что нет необходимости фактически выполнять операцию 'store'.

"= &" определяет их как early-clobber операнды,Они могут быть записаны до того, как все входы будут использованы.Поэтому, если компилятор может свободно выбирать входной регистр, он не должен псевдоним этих двух.

Это технически необязательно для %ecx, так как он явно назван как вход: "0" (n) - количество повторений в этом случае.Я не уверен, что это необходимо и для %edi, поскольку он не может быть обновлен до того, как введен "1" (s), и инструкция не выполнена.И опять же, поскольку явно названо как вход, компилятор не может выбрать другой регистр.Короче говоря, «= &» здесь не помешает, но ничего не делает.

Поскольку "a" (c) указывает только для ввода регистр %eax, установленный на (c), компилятор может предположить, что %eax все еще содержит это значение после «asm» - что действительно имеет место с "rep; stosb;".

"memory" указывает, что память может быть изменена способом, неизвестнымкомпилятор - что верно в этом случае, он устанавливает (n) байтов, начиная с (r), в значение (c) - предполагая, что флаг направления очищен, что и должно быть.Это имеет эффект принудительной перезагрузки значений, так как компилятор не может предположить, что регистры отражают значения памяти, которые они должны больше.Это не повредит, и может потребоваться сделать его безопасным для общего случая memset, но это часто излишне.

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

Вы не можете писать описание клоббера так, чтобы оно совпадало с операндом ввода или вывода.Например, у вас может не быть операнда, описывающего класс регистров с одним членом, если вы упомянули этот регистр в списке clobber.

...