В третьей секции задаются входные операнды. Строка адресации такого операнда не должна содержать знака равенства, в остальном синтаксис совпадает с синтаксисом выходных операндов.
Если требуется указать, что в одной инструкции осуществляется как чтение регистра, так и запись в него, необходимо в строке адресации входного операнда поставить номер выходного операнда. Например, если входной регистр должен быть тем же, что и регистр первого выходного операнда, назначьте ему номер 0. Выходные операнды нумеруются слева направо, начиная с нуля. Если просто указать одинаковое C-выражение для входного и выходного операндов, то это еще не означает, что оба значения будут помещены в один и тот же регистр.
Данную секцию можно пропустить, если входные операнды отсутствуют и следующая секция модифицируемых регистров пуста.
9.3.4. Модифицируемые регистры
Если в качестве побочного эффекта инструкция модифицирует значение одного или нескольких регистров, в функции asm()
должна присутствовать четвертая секция. Например, инструкция fucomip
меняет регистр кода завершения, обозначаемый как cc. Строки, представляющие затираемые регистры, разделяются запятыми. Если инструкция способна изменить произвольную ячейку памяти, в этой секции должно стоять ключевое слово memory
. На основании этой информации компилятор определяет, какие значения должны быть загружены повторно после завершения функции asm()
. При отсутствии данной секции компилятор может сделать неверное предположение о том, что регистры содержат прежние значения, и это скажется на работе программы.
9.4. Пример
В архитектуре x86 есть инструкции, определяющие позицию старшего и младшего значащих битов в слове. Процессор выполняет эти инструкции очень быстро. С другой стороны, чтобы сделать то же самое на языке С, потребуется написать цикл с операциями побитового сдвига.
Инструкция bsrl
вычисляет местоположение старшего значащего бита в первом операнде и записывает результат (номер позиции начиная с нуля) во второй операнд. Например, следующая команда анализирует переменную number и помещает результат в переменную position
:
asm("bsrl %1, %0" : "=r" (position) : "r" (number)};
Ей соответствует такой фрагмент на языке С:
long i;
for (i = (number >> 1), position = 0; i != 0; ++position)
i >>= 1;
Чтобы сравнить скорость выполнения двух фрагментов, мы поместили их в цикл, где перебирается большое количество чисел. В листинге 9.1 приведена реализация на языке С. Программа перебирает значения от единицы до числа, указанного в командной строке. Для каждого значения переменной number вычисляется позиция старшего значащего бита. В листинге 9.2 показано, как сделать то же самое с помощью ассемблерной вставки. Обратите внимание на то, что в обоих случаях результат вычислений заносится в переменную result
, объявленную со спецификатором volatile
. Это необходимо для подавления оптимизации со стороны компилятора, который удалит весь блок вычислений, если их результаты не используются или не заносятся в память.
#include
#include
int main(int argc, char* argv[]) {
long max = atoi(argv[1]);
long number;
long i;
unsigned position;
volatile unsigned result;
/* Повторяем вычисления для большого количества чисел. */
for (number = 1; number <= max; ++number) {
/* Сдвигаем число вправо, пока результат не станет
равным нулю.
Запоминаем количество операций сдвига. */
for (i = (number >> 1), position = 0; i != 0; ++position)
i >>= 1;
/* Позиция старшего значащего бита — это общее число
операций сдвига, кроме первой. */
result = position;
}
return 0;
}
bsrl
#include
#include
int main(int argc, char* argv[]) {
long max = atoi(argv[1]);
long number;
unsigned position;
volatile unsigned result;
/* Повторяем вычисления для большого количества чисел. */
for (number = 1; number <= max; ++number) {
/* Вычисляем позицию старшего значащего бита с помощью