2013-05-22 7 views
2

Я написал простую программу, содержащую встроенный ассемблерный код. Мой код просто добавляет переменные a и b и возвращает результат в b.Модификаторы ограничения сборки inline = и +

Что меня смущает, почему код ниже генерирует эту инструкцию movl 28 (% esp),% ecx.

Я не полностью выполняю роли модификаторов + и = играю во входных и выходных списках. Итак, было бы полезно, если бы вы могли пролить свет на это.

#include <cstdio> 

int main(int argc , char ** argv) 
{ 
    int a = 2, b = 7; 

    __asm__ 
    (
    "addl %1,%0;"  
    :"+r"(b)   
    :"r"(a), "r"(b)  
    ); 

    printf("b = %d\n",b); 

    return 0; 
} 
movl $2, 24(%esp) 
    movl $7, 28(%esp) 

    movl 24(%esp), %edx 
    movl 28(%esp), %ecx 
    movl 28(%esp), %eax 

    addl %edx,%eax 

    movl %eax, 28(%esp) 

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

Хорошо, теперь я изменил с + r на = r. И это код сборки GCC.

#include <cstdio> 

int main(int argc , char ** argv) 
{ 
    int a = 2, b = 7; 

    __asm__ 
    (
    "addl %1,%0;"  
    :"=r"(b)   
    :"r"(a), "r"(b)  
    ); 

    printf("b = %d\n",b); 

    return 0; 
} 
movl $2, 24(%esp) 
    movl $7, 28(%esp) 
    movl 24(%esp), %eax 
    movl 28(%esp), %edx 

    addl %eax,%eax; 

    movl %eax, 28(%esp) 

Теперь выход 4, который является неправильным. Мой вопрос: почему с «= r» GCC решил повторно использовать регистр eax для b, как показано в этой инструкции addl% eax,% eax;

ответ

5

Смущает меня почему ниже код генерирует эта инструкция movl 28(%esp), %ecx

Потому что вы перечислили b как в двух отдельных входных регистров; + в первом разделе означает, что сборка считывает и изменяет регистр. Таким образом, он загружается в два регистра, хотя сборка не использует вторую.

Собрание должно быть просто:

"addl %1,%0;" : "+r"(b) : "r"(a) 

Я не полностью undertand роли модификаторов + и = играть во входных и выходных списков.

+ означает, что регистр считывается и записывается, так что он должен иметь значение переменной до начала сборки. = означает, что он просто написан и может иметь любое значение перед сборкой.

Для получения более подробной информации см. documentation.

Мой вопрос: почему с «= r» GCC решил использовать регистр eax для b, как показано в этой инструкции addl% eax,% eax;

Потому что теперь ваши ограничения ошибочны. Вы говорите компилятору, что вы только пишете во второй операнд команды addl (%0), поэтому он предполагает, что он может использовать тот же регистр, что и один из входов. Фактически, этот операнд также является входом для addl. Затем вы все еще говорите, что вам нужна вторая копия b в отдельном регистре, который сборка не использует.

Как было сказано выше, использовать "+r"(b) в первом списке (выход), чтобы указать, что этот %0 является b и используется для ввода и вывода, и "r"(a) во втором списке (вход), чтобы указать, что %1 является a и используется только для ввода. Не помещайте третий регистр, так как в сборке нет %2.

+0

спасибо за ваш ответ. Я поняла концепцию. Но у меня есть еще один пример. Я скоро обновляю свой пост с помощью нового примера. –

+0

@takwing: Я обновил новый вопрос. Короткий ответ: если ограничения регистра не соответствуют тому, что делает сборка, то распределения регистров будут фиктивными. –

+0

Большое спасибо за ваш ответ. Понятно об этом. Что касается документации, я читал ее снова и снова, но я все еще не понимал всей концепции. Это то, что документация говорит об этих двух модификаторах: = ' Означает, что этот операнд предназначен только для записи для этой команды: предыдущее значение отбрасывается и заменяется выходными данными. '+' Означает, что этот операнд считывается и записывается инструкцией. То, что меня смущает и все еще путает, - это использование слова «эта инструкция». Однако внутри asm() может быть последовательность инструкций по сборке. –

1

Вы не должны использовать "r" для повторения b - используйте "0" (номер выходного аргумента). Теперь компилятор загружает b в %ecx, который не используется.

+0

Каково значение повторения r в «r» (b)? Почему это приводит к загрузке GCC b в другой регистр, который является ECX в этом случае? –

+0

Да, это то, что он делает. –

-1

«Я написал простую программу, содержащую встроенный код сборки.»

«Мой код просто добавляет переменные а и б и возвращает результат в б. бла-бла-бла»

Где?

mov ax,2 
mov bx,3 

add bx,ax 

Вы можете называть меня Эйнштейна, если вы хотите ...

Что напоминает мне о "обмане" интервью

Читать и плакать

http://www-users.cs.york.ac.uk/susan/joke/cpp.htm

3

Его довольно просто - + означает ввод и вывод в то время как = означает выход только. Поэтому, когда вы говорите:

asm("addl %1,%0;" : "+r"(b) : "r"(a), "r"(b)); 

У вас есть три операнда. Один регистр ввода/вывода (%0), инициализированный b, и вывод его в b, а также два входных регистра (%1 и %2), инициализированные a и b соответственно. Теперь вы никогда не используете %2, но его там все равно. Вы можете увидеть его в коде производства:

movl 24(%esp), %edx 
movl 28(%esp), %ecx 
movl 28(%esp), %eax 

addl %edx,%eax 

movl %eax, 28(%esp) 

Так компилятор использовал %eax для %0, %edx для %1 и %ecx для %2. Все три загружаются перед встроенным кодом, и после этого записывается %0.

Когда вы делаете:

asm("addl %1,%0;" : "=r"(b) : "r"(a), "r"(b)); 

Теперь %0 выводится только (не вход).Таким образом, компилятор может производить:

movl 24(%esp), %eax 
movl 28(%esp), %edx 

addl %eax,%eax; 

movl %eax, 28(%esp) 

использованием %eax для %0 и %1 и %edx для %2.

Другим способом получить то, что вы хотите:

asm("addl %1,%0;" : "=r"(b) : "r"(a), "0"(b)); 

используя 0 ограничение на %2 означает, что компилятор должен поместить его в том же месте, как %0 Так заканчиваются генераторные:

movl 24(%esp), %edx 
movl 28(%esp), %eax 

addl %edx,%eax 

movl %eax, 28(%esp) 

использованием %eax для %0 и %2 и %edx для %1

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