будет преобразован в такую последовательность команд x86:
movl -8(%ebp),%edx
movl -4(%ebp),%ecx
#APP
mycool_asm %edx, %edx
#NO_APP
movl %edx,-16(%ebp)
movl %ecx,-12(%ebp)
Переменные foo
и bar
занимают по два слова в стеке в 32-разрядной архитектуре x86. Регистр ebp
ссылается на данные, находящиеся в стеке.
Первые две команды копируют переменную foo в регистры edx
и ecx
, с которыми работает инструкция mycool_asm
. Компилятор решил поместить результат в те же самые регистры. Последние две команды копируют результат в переменную bar
. Выбор нужных регистров и копирование операндов осуществляются автоматически.
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__
.
9.3.2. Выходные операнды
Во второй секции указаны выходные операнды инструкции. Каждому операнду соответствует строка адресации и выражение языка С, записанное в скобках. В случае выходных операндов (все они должны быть левосторонними значениями) строка адресации должна начинаться со знака равенства. Компилятор проверяет, действительно ли каждый выходной операнд является левосторонним значением (т.е может стоять в левой части оператора присваивания).
Список обозначений регистров для конкретной архитектуры можно найти в исходных текстах компилятора 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
. Если выходные операнды отсутствуют, но есть входные операнды или модифицируемые регистры, то вторую секцию следует оставить пустой или пометить ее комментарием наподобие /* нет выходных данных */
.
9.3.3. Входные операнды