GCC Inline AssemblyGCC Inline Assembly — Встроенный ассемблер компилятора GCC, представляющий собой язык макроописания интерфейса компилируемого высокоуровневого кода с ассемблерной вставкой. ОсобенностиСинтаксис и семантика GCC Inline Assembly имеет следующие существенные отличия:
Предварительные сведенияДля понимания работы GCC Inline Assembly, следует хорошо понимать действия, выполняемые в процессе компиляции. В начале gcc вызывает препроцессор cpp, который включает заголовочные файлы, разворачивает все условные директивы и выполняет макроподстановки. Посмотреть, что получилось после макроподстановки, можно командой Затем gcc анализирует полученный код, на этой же фазе производит оптимизацию кода и в итоге производит ассемблерный код. Увидеть сгенерированный ассемблерный код можно командой Затем gcc вызывает ассемблер gas для того, чтобы он создал из ассемблерного кода объектный код. Обычно ключ -c (compile only) используется в проектах, состоящих из многих файлов. Затем gcc вызывает компоновщик ld для сборки исполняемого файла из полученных объектных файлов. Для иллюстрации данного процесса создадим файл test.c следующего содержания: int main()
{
asm ("Bla-Bla-Bla"); // вставим такую инструкцию
return 0;
}
Если при компиляции выдается предупреждение -Wimplicit-function-declaration "Неявная декларация функции asm", используйте: __asm__ ("Bla-Bla-Bla");
Если мы скажем выполнить
Сообщение исходит именно от Ассемблера. Отсюда следует важный вывод: GCC никак не интерпретирует содержимое ассемблерной вставки, воспринимая её как макроподстановку времени компиляции. СинтаксисОбщая структураОбщая структура ассемблерной вставки выглядит следующим образом: asm [volatile] («команды и директивы ассемблера» : выходные параметры : входные параметры : изменяемые параметры); впрочем, существует и более короткая форма: asm [volatile] («команды ассемблера»); Синтаксис командОсобенностью ассемблера gas и компилятора gcc является тот факт, что они используют непривычный для x86 синтаксис AT&T, который существенно отличается от синтаксиса Intel. Основные отличия[1]:
Обычно пренебрегаемый факт того, что внутри директивы asm могут находиться не просто ассемблерные команды, но и вообще любые директивы, распознаваемые gas, может сослужить хорошую службу. Например, можно вставить содержимое двоичного файла в итоговый объектный код: asm(
"our_data_file:\n\t"
".incbin \"some_bin_file.txt\"\n\t" // используем директиву .incbin
"our_data_file_len:\n\t"
".long .-our_data_file\n\t" // вставляем значение .long с вычисленной длиной файла
);
И затем адресоваться к этому бинарному файлу: extern char our_data_file[];
extern long our_data_file_len;
Как работает макроподстановкаКонструкция: asm ("movl %0,%%eax"::"i"(1));
Превратится в movl $1,%eax
Входные и выходные параметрыМодификаторыТонкие моментыКлючевое слово volatileКлючевое слово volatile служит для того чтобы указать компилятору, что вставляемый ассемблерный код может давать побочные эффекты, поэтому попытки оптимизации могут привести к логическим ошибкам. Случаи, когда ключевое слово volatile ставить обязательно: Допустим, внутри цикла имеется ассемблерная вставка, производящая проверку на занятость глобальной переменной и ожидания в спинлоке её освобождения. Когда компилятор начинает оптимизировать цикл, он выкидывает из цикла всё, что явным образом в цикле не изменяется. Поскольку в этом случае оптимизирующий компилятор не видит явной зависимости между параметрами ассемблерной вставки и переменными, изменяющимися в цикле, ассемблерная вставка может быть выкинута из цикла со всеми вытекающими последствиями. СОВЕТ: Всегда указывайте asm volatile в тех случаях, когда ваша ассемблерная вставка должна «стоять там, где стоит». Особенно это касается тех случаев, когда вы работаете с атомарными примитивами. «memory» в clobber listСледующий «тонкий момент» — явное указание «memory» в clobber list. Помимо простого указания компилятору, что ассемблерная вставка изменяет содержимое памяти, она ещё служит директивой Memory Barrier для компилятора. Это означает, что те операции обращений в память, которые стоят выше по коду, в результирующем машинном коде будут выполняться до тех, которые стоят ниже ассемблерной вставки. В случае многопоточной среды, когда от этого напрямую зависит риск возникновения race condition, это обстоятельство является существенным. СОВЕТ № 1: Быстрый способ сделать Memory Barrier #define mbarrier() asm volatile ("":::"memory")
СОВЕТ № 2: Указание «memory» в clobber list не только «хороший тон», но и в случае работы с атомарными операциями, призванными разрулить race condition, является обязательным. Примеры использованияint main()
{
int sum = 0, x = 1, y = 2;
asm ( "add %1, %0" : "=r" (sum) : "r" (x), "0" (y) ); // sum = x + y;
printf("sum = %d, x = %d, y = %d", sum, x, y); // sum = 3, x = 1, y = 2
return 0;
}
Примечания
Ссылки
|
Portal di Ensiklopedia Dunia