Если под «обнулением» вы подразумеваете «значения в регистрах заменены на 0, чтобы я не знал, что делает какая-то другая функция», то нет, регистры не обнуляются перед использованием. Но это не должно иметь значения, потому что вы говорите GCC, что вы планируете хранить информацию там, а не то, что вы хотите читать информацию, которая в данный момент там есть.
Вы передаете эту информацию в GCC, чтобы (читая документацию) «вам не нужно было угадывать, какие регистры или области памяти будут содержать данные, которые вы хотите использовать», когда вы закончите с кодом сборки (например, вы не будете не нужно помнить, будут ли данные в регистре стека или в каком-либо другом регистре).
GCC нужна большая помощь для ассемблерного кода, потому что "Компилятор ... не анализирует шаблон инструкции ассемблера и не знает, что он означает или даже является ли он допустимым вводом ассемблера. Чаще всего используется расширенная функция asm для машинных инструкций сам компилятор не знает о существовании. "
Обновление
GCC спроектирован как многопроходный компилятор. Многие из пропусков на самом деле совершенно разные программы. Набор программ, образующих «компилятор», переводит ваш исходный код из C, C ++, Ada, Java и т. Д. В код сборки. Затем отдельная программа (gas
для GNU Assembler) берет этот ассемблерный код и превращает его в двоичный файл (а затем ld
и collect2
делают больше вещей с двоичным файлом). Сборочные блоки существуют для прямой передачи текста в gas
, и существует список clobber (и входной список), так что компилятор может делать все, что нужно для передачи информации между сторонами C, C ++, Ada, Java и т. Д. вещей и стороны вещей gas
, а также для гарантии того, что любая важная информация, находящаяся в настоящее время в регистрах, может быть защищена от сборочного блока путем копирования ее в память перед запуском сборочного блока (и последующего копирования обратно из памяти).
Альтернативой может быть сохранение и восстановление каждого регистра для каждого блока кода сборки. На RISC-машине с большим количеством регистров, которые могут стоить дорого (например, Itanium имеет 128 регистров общего назначения, еще 128 регистров с плавающей запятой и 64 1-битных регистра).
Прошло много времени с тех пор, как я написал любой ассемблерный код. И у меня гораздо больше опыта использования функции именованных регистров в GCC, чем при работе с конкретными регистрами. Итак, глядя на пример:
#include <stdio.h>
long foo(long l)
{
long result;
asm (
"movl %[l], %[reg];"
"incl %[reg];"
: [reg] "=r" (result)
: [l] "r" (l)
);
return result;
}
int main(int argc, char** argv)
{
printf("%ld\n", foo(5L));
}
Я запросил выходной регистр, который я назову reg
внутри кода сборки, и этот GCC автоматически скопирует в переменную result
по завершении. Нет необходимости присваивать этой переменной разные имена в C-коде по сравнению с ассемблерным; Я только сделал это, чтобы показать, что это возможно. Какой физический регистр GCC решит использовать - будь то %%eax
, %%ebx
, %%ecx
и т. Д. - GCC позаботится о копировании любых важных данных из этого регистра в память при вводе блока сборки, чтобы я мог полностью использовать этот регистр до конца блока сборки.
Я также попросил регистр ввода, который я назову l
как в Си, так и в сборке. GCC обещает, что любой физический регистр, который он решит дать мне, будет иметь значение в настоящее время в переменной C l
, когда я войду в блок сборки. GCC также выполнит все необходимые записи для защиты любых данных, которые попадают в этот регистр, прежде чем я войду в блок сборки.
Что если я добавлю строку в код сборки? Скажи:
"addl %[reg], %%ecx;"
Поскольку компилятор GCC не проверяет код сборки, он не защитит данные в %%ecx
. Если мне повезет, %%ecx
может оказаться одним из регистров, которые GCC решил использовать для %[reg]
или %[l]
. Если мне не повезет, я «загадочным образом» изменил значение в какой-то другой части моей программы.