Программирование для Linux. Профессиональный подход
Шрифт:
9.1. Когда необходим ассемблерный код
Инструкции, указываемые в функции
Прибегать к ассемблерным инструкциям как к средству ускорения работы программы следует лишь в крайнем случае. Современные компиляторы достаточно сложны и прекрасно осведомлены об особенностях работы процессоров, для которых они генерируют код. Часто они создают цепочки кодов, которые кажутся неэффективными или неоптимальными, но на самом деле такие последовательности инструкций выполняются быстрее других. В подавляющем большинстве случаев можно положиться на оптимизирующие способности компиляторов.
Иногда одна или две ассемблерные команды способны заменить целую группу высокоуровневых инструкций. Например, чтобы определить позицию самого старшего значащего бита целого числа в языке С, требуется написать цикл, тогда как во многих ассемблерных языках для этой цели существует операция
9.2. Простая ассемблерная вставка
Вот как с помощью функции
Выражение в скобках состоит из секций, разделенных двоеточиями. В первой секции указана ассемблерная инструкция и ее операнды. Команда
Во второй секции задаются выходные операнды. Единственный такой операнд будет помещен в C-переменную
В третьей секции перечислены входные операнды. Переменная
Выражение
9.2.1. Преобразование функции asm в ассемблерные инструкции
Компилятор
Например, следующий фрагмент программы:
будет преобразован в такую последовательность команд x86:
Переменные
Первые две команды копируют переменную foo в регистры
9.3. Расширенный синтаксис ассемблерных вставок
В следующих подразделах будет описан синтаксис правил, по которым строятся выражения в функции
Сначала инструкция
9.3.1. Ассемблерные инструкции
Первая секция содержит ассемблерные инструкции, заключенные в кавычки. В рассматриваемом примере таких инструкций две:
Компилятор игнорирует содержимое первого раздела, разве что один уровень символов процента удаляется, т.е. вместо
Если при компиляции программы, содержащей функцию
9.3.2. Выходные операнды
Во второй секции указаны выходные операнды инструкции. Каждому операнду соответствует строка адресации и выражение языка С, записанное в скобках. В случае выходных операндов (все они должны быть левосторонними значениями) строка адресации должна начинаться со знака равенства. Компилятор проверяет, действительно ли каждый выходной операнд является левосторонним значением (т.е может стоять в левой части оператора присваивания).
Список обозначений регистров для конкретной архитектуры можно найти в исходных текстах компилятора
Таблица 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) |