9.3. Расширенный синтаксис ассемблерных вставок
В следующих подразделах будет описан синтаксис правил, по которым строятся выражения в функции asm(). Секции выражения отделяются друг от друга двоеточиями. Мы будем ссылаться на следующую инструкцию, которая вычисляет результат булевого выражения x > y:
asm("fucomip %%st(1), %%st; seta %%al" :
"=a" (result) : "u" (y), "t" (x) : "cc", "st");
Сначала инструкция fucomipсравнивает два операнда, xи y, и помещает значение, обозначающее результат, в регистр cc, после чего инструкция setaпреобразует это значение в 0 или 1.
9.3.1. Ассемблерные инструкции
Первая секция содержит ассемблерные инструкции, заключенные в кавычки. В рассматриваемом примере таких инструкций две: fucomipи seta. Они разделены точкой с запятой. Если текущий вариант языка ассемблера не допускает такого способа разделения инструкций, воспользуйтесь символом новой строки ( \n).
Компилятор игнорирует содержимое первого раздела, разве что один уровень символов процента удаляется, т.е. вместо %%будет %. Смысл выражения %%st(1)и ему подобных зависит от архитектуры компьютера.
Если при компиляции программы, содержащей функцию asm(), указать опцию -traditionalили -ansi, компилятор gccвыдаст предупреждение. Чтобы этого избежать, используйте альтернативное имя __asm__.
Во второй секции указаны выходные операнды инструкции. Каждому операнду соответствует строка адресации и выражение языка С, записанное в скобках. В случае выходных операндов (все они должны быть левосторонними значениями) строка адресации должна начинаться со знака равенства. Компилятор проверяет, действительно ли каждый выходной операнд является левосторонним значением (т.е может стоять в левой части оператора присваивания).
Список обозначений регистров для конкретной архитектуры можно найти в исходных текстах компилятора gcc(конкретнее — в определении макроса REG_CLASS_FROM_LETTER). Например, в файле gcc/config/i386/i386.hсодержатся обозначения, соответствующие архитектуре x86 (табл. 9.1).
Таблица 9.1. Обозначения регистров в архитектуре Intel x86
| Символ регистра |
Регистры, которые могут использоваться компилятором gcc |
| R |
Регистры общего назначения (EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP) |
| q |
Общие регистры хранения данных (EAX, ЕВХ, ECX, EDX) |
| f |
Регистр для чисел с плавающей запятой |
| t |
Верхний стековый регистр для чисел с плавающей запятой |
| u |
Второй после верхнего стековый регистр для чисел с плавающей запятой |
| a |
Регистр EAX |
| b |
Регистр EBX |
| с |
Регистр ECX |
| d |
Регистр EDX |
| x |
Регистр SSE (регистр потокового расширения SIMD) |
| y |
Мультимедийные регистры MMX |
| A |
Восьмибайтовое значение, формируемое из регистров EAX и EDX |
| D |
Указатель приемной строки в строковых операциях (EDI) |
| S |
Указатель исходной строки в строковых операциях (ESI) |
Если есть несколько однотипных операндов, то они разделяются запятыми, как показано в секции входных операндов. Всего можно задавать до десяти операндов, адресуемых как %0, %1, … %9. Если выходные операнды отсутствуют, но есть входные операнды или модифицируемые регистры, то вторую секцию следует оставить пустой или пометить ее комментарием наподобие /* нет выходных данных */.
В третьей секции задаются входные операнды. Строка адресации такого операнда не должна содержать знака равенства, в остальном синтаксис совпадает с синтаксисом выходных операндов.
Если требуется указать, что в одной инструкции осуществляется как чтение регистра, так и запись в него, необходимо в строке адресации входного операнда поставить номер выходного операнда. Например, если входной регистр должен быть тем же, что и регистр первого выходного операнда, назначьте ему номер 0. Выходные операнды нумеруются слева направо, начиная с нуля. Если просто указать одинаковое C-выражение для входного и выходного операндов, то это еще не означает, что оба значения будут помещены в один и тот же регистр.
Данную секцию можно пропустить, если входные операнды отсутствуют и следующая секция модифицируемых регистров пуста.
9.3.4. Модифицируемые регистры
Читать дальше