Что такое r () и двойной процент %% в встроенном ассемблере GCC? - PullRequest
5 голосов
/ 28 августа 2010

Пример:

int main(void)
{
    int x = 10, y;

    asm ("movl %1, %%eax;"
        "movl %%eax, %0;"
        :"=r"(y)    /* y is output operand */
        :"r"(x)     /* x is input operand */
        :"%eax");   /* %eax is clobbered register */
}
  • что такое r(y)?
  • и почему %% используется до eax?Как правило, один % используется правильно?

Ответы [ 4 ]

6 голосов
/ 28 августа 2010

Хорошо, это встроенный ассемблер gcc, который очень мощный, но трудный для понимания.

Во-первых,% char - это специальный char. Это позволяет вам определять регистр и номер заполнителей (более подробно об этом позже). К сожалению,% также используется как часть имени регистра (например,% EAX), поэтому в встроенном ассемблере gcc вы должны использовать два процента символов, если хотите назвать регистр.

% 0,% 1 и% 2 (и т. Д.) Являются входными и выходными операндами-заполнителями. Они определены в списке, за которым следует строка ассемблера. В вашем примере% 0 становится заполнителем для y, а% 1 становится заполнителем для x. Компилятор будет следить за тем, чтобы переменные были в регистрах для входных операндов до того, как исполняется asm-код, и он будет гарантировать, что выходной операнд будет записан в переменную, указанную в списке выходных операндов.

Теперь вы должны понять, что такое r (y): это входной операнд, который резервирует регистр для переменной y и присваивает его заполнителю% 1 (поскольку это второй операнд, указанный после строки встроенного ассемблера ). Есть много других типов заполнителей. m позволяет вам указать место в памяти, и если я не ошибаюсь, я могу быть использован для числовых констант. Вы найдете их все в документации gcc.

Тогда есть список клоббер. Этот список важен! В нем перечислены все регистры, флаги и т. Д., Которые модифицируются в вашем коде ассемблера (например, EAX в вашем примере). Если вы ошибетесь, оптимизатор не узнает, что было изменено, и очень вероятно, что вы получите код, который не работает.

Ваш пример, кстати, почти бессмыслен. Он просто загружает значение X в регистр и присваивает этот регистр EAX. После этого EAX сохраняется в другом регистре, который впоследствии станет вашей переменной y. Так что все, что он делает, это простое задание:

y = x;

И последнее: если вы раньше работали с ассемблером в стиле Intel: вы должны прочитать аргументы в обратном направлении. Для всех инструкций исходным операндом является тот, который следует за самой инструкцией, а целевой операнд - справа от запятой. По сравнению с синтаксисом Intel это совсем наоборот.

1 голос
/ 28 августа 2010

Строки с «r» или «= r» являются ограничениями операнда.«=» Означает выходной операнд.По сути, это:

:"=r"(y)
:"r"(x)

означает, что% 0 (то есть: первый операнд) соответствует y и предназначен для вывода, а% 1 (второй операнд) соответствует x.

Один% обычно используется в синтаксической сборке AT & T, но для встроенной сборки один% используется для ссылок операндов (например,% 0,% 1), в то время как двойной% используется для ссылок на регистр букв.Подумайте об этом, как о том, как вы должны использовать двойной% в формате printf, если вы хотите, чтобы в выводе был буквальный%.

Сжатый регистр - это регистр, значение которого будет изменено кодом сборки.Как видно из кода, в eax записывается.Вы должны сообщить об этом gcc, чтобы он знал, что скомпилированный код не может сохранить в eax все, что ему нужно для дальнейшего использования, когда он собирается вызвать эту сборку.

0 голосов
/ 28 августа 2010

Попробуйте этот урок .Он охватывает все, что вы просите: например, попробуйте section 6 - он достаточно хорошо объясняет ограничения и для чего предназначен знак "=".Даже концепция засоренных регистров покрыта (раздел 5.3).

0 голосов
/ 28 августа 2010

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

В директивах asm, подобных этой, когда вы пишете сборку, вы выясняете, какие регистры будут перекрыты ею; Затем вы сообщаете об этом компилятору (как показано в вашем примере), и компилятор делает то, что должен, чтобы сохранить текущее значение этого регистра, если это необходимо. Компилятор много знает о том, как значения в регистрах и других местах будут использоваться для последующих вычислений, но он обычно не может анализировать встроенную сборку. Таким образом, вы выполняете анализ самостоятельно, а компилятор использует информацию о блокировке, чтобы безопасно включить сборку в свои варианты оптимизации.

...