Один из них генерируется компилятором, потому что вы попросили G CC предоставить данные для выбора регистра для вас. Вот что означает "r"(x)
. И вы скомпилировали с отключенной оптимизацией (по умолчанию -O0
), так что она фактически сохранила x
в памяти, а затем перезагрузила ее перед вашим оператором asm.
В вашем коде нет никакого дела, предполагая что-либо о содержимом памяти или где EBP указывает.
Поскольку вы используете 89 c0 mov %eax,%eax
, , единственными безопасными ограничениями для вашего оператора asm являются "a"
ограничения явного регистра для ввода и вывода, заставляет компилятор выбрать это. Если вы компилируете с включенной оптимизацией, ваш код полностью сломается, потому что вы соврали компилятору о том, что на самом деле делает ваш код.
// constraints that match your manually-encoded instruction
asm (".byte 0x89, 0xC0\n\t"
: "=a" (y)
: "a" (x)
);
Там нет ограничение заставляет G CC выбирать определенный режим адресации для источника "m"
или операнда "=m"
dest, поэтому вам нужно запрашивать входы / выходы в указанных c регистрах.
Если вы хотите чтобы кодировать ваши собственные mov
инструкции в отличие от от стандартного mov, см. , какие инструкции MOV в x86 не используются или используются реже всего, a nd может использоваться для пользовательского расширения MOV - вы можете использовать префикс перед обычными опкодами mov
, чтобы вы могли позволить ассемблеру кодировать регистры и режимы адресации, например .byte something; mov %1, %0
.
Посмотрите на вывод asm, сгенерированный компилятором (gcc -S
, а не дизассемблирование .o
или исполняемого файла). Затем вы можете увидеть, какие инструкции взяты из оператора asm, а какие - G CC.
Если вы явно не ссылаетесь на некоторые операнды в шаблоне asm, но по-прежнему хотите увидеть, что выбрал компилятор, вы можете использовать их в asm комментариях, таких как:
asm (".byte 0x8b,0x45,0xf8 # 0 = %0 1 = %1 \n\t"
".byte 0x89, 0xC0\n\t"
: "=r" (y)
: "r" (x)
);
и g cc заполнит его для вас, чтобы вы могли видеть, какие операнды он ожидает Вы должны читать и писать. ( Годболт с g++ -m32 -O3
). Я поместил ваш код в void foo(){}
вместо main
, потому что G CC -m32 считает, что нужно перестроить стек в верхней части main. Это усложняет выполнение кода.
# gcc-9.2 -O3 -m32 -fverbose-asm
.LC0:
.string "dst value : %d\n"
foo():
subl $20, %esp #,
movl $5, %eax #, tmp84
## Notice that GCC hasn't set up EBP at all before it runs your asm,
## and hasn't stored x in memory.
## It only put it in a register like you asked it to.
.byte 0x8b,0x45,0xf8 # 0 = %eax 1 = %eax # y, tmp84
.byte 0x89, 0xC0
pushl %eax # y
pushl $.LC0 #
call printf #
addl $28, %esp #,
ret
Также обратите внимание, что если вы компилируете как 64-битный код, он, вероятно, выберет %esi
в качестве регистра, потому что printf будет нуждаться во втором аргументе. Таким образом, ограничение "a"
вместо "r"
действительно имеет значение.
Вы можете получить 32-битный G CC, чтобы использовать другой регистр, если вы присваиваете переменную, которая должна сохраняться в функции вызов; тогда G CC выберет регистр с сохранением вызовов, такой как EBX, вместо EAX.