GCC Inline Assembly

Поделись знанием:
Перейти к: навигация, поиск

GCC Inline Assembly — Встроенный ассемблер компилятора GCC, представляющий собой язык макроописания интерфейса компилируемого высокоуровнего кода с ассемблерной вставкой.





Особенности

Синтаксис и семантика GCC Inline Assembly имеет следующие существенные отличия:

  • GCC не интерпретирует никак содержимое ассемблерной вставки.
  • Служит явное описание интерфейса с ассемблерной вставкой.
  • Даёт компилятору возможность свободы выбора регистров.
  • Позволяет явно указать на имеющиеся побочные действия ассемблерного кода.
  • Позволяет использовать все инструкции (и директивы тоже) которые распознает ассемблер, а не только те, что знает и применяет gcc

Предварительные сведения

Для того, чтобы хорошо понимать, как работает GCC Inline Assembly для начала неплохо сначала хорошо представлять процесс компиляции, как он происходит.

В начале gcc вызывает препроцессор cpp, который включает заголовочные файлы, разворачивает все условные директивы и выполняет макроподстановки. Посмотреть, что получилось после макроподстановки, можно командой gcc -E -o preprocessed.c some_file.c. Ключ -E редко используется, в основном когда вы занимаетесь отладкой макросов.

Затем gcc анализирует полученный код, на этой же фазе производит оптимизацию кода и в итоге производит ассемблерный код. Увидеть сгенерированный ассемблерный код можно командой gcc -S -o some_file.S some_file.c.

Затем 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 -S -o test.S test.c, то мы обнаружим важный факт: компилятор обработал «неправильную» инструкцию и результирующий ассемблерный файл test.S содержит нашу строку «Bla-Bla-Bla». Однако, если мы попробуем создать объектный код или собрать бинарный файл, то gcc выведет следующее:

test.c: Assembler messages: test.c:3: Error: no such instruction: 'Bla-Bla-Bla'

Сообщение исходит именно от Ассемблера.

Отсюда следует важный вывод: GCC никак не интерпретирует содержимое ассемблерной вставки, воспринимая её как макроподстановку времени компиляции.

Синтаксис

Общая структура

Общая структура ассемблерной вставки выглядит следующим образом:

asm [volatile] («команды и директивы ассемблера» : выходные параметры : входные параметры : изменяемые параметры);

впрочем, существует и более короткая форма:

asm [volatile] («команды ассемблера»);

Синтаксис команд

Особенностью ассемблера gas и компилятора gcc есть тот факт, что они используют непривычный для x86 синтаксис AT&T, который существенно отличается от синтаксиса Intel. Основные отличия[1]:

  1. Порядок операндов: Операция Источник,Приёмник.
  2. Названия регистров имеют явный префикс %, указывающий, что это регистр. Это позволяет работать с переменными, которые имеют то же название, что и какой-либо регистр, что невозможно в Intel-синтаксисе, у которого префиксы для регистров не используются, а их названия являются зарезервированными ключевыми словами.
  3. Явное задание размеров операндов в суффиксах команд: b-byte, w-word, l-long, q-quadword. В командах типа movl %edx,%eax это может показаться излишним, однако является весьма наглядным средством, когда речь идёт о incl (%esi) или xorw $0x7,mask
  4. Названия констант начинаются с $ и могут быть выражением. Например movl $1,%eax
  5. Значение без префикса означает адрес. Например:
    movl $123,%eax — записать в %eax число 123,
    movl 123,%eax — записать в %eax содержимое ячейки памяти с адресом 123,
    movl var,%eax — записать в %eax значение переменной var,
    movl $var,%eax — загрузить адрес переменной var
  6. Для косвенной адресации необходимо использовать круглые скобки. Например movl (%ebx),%eax — загрузить в %eax значение переменной, по адресу находящемуся в регистре %ebx
  7. SIB-адресация: смещение(база, индекс, множитель)

Обычно игнорируемый факт того, что внутри директивы 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 Barier
#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;
}
  • код: добавить %1 к %0 и сохранить результат в %0
  • выходные параметры: универсальный регистр, сохранённый в локальную переменную, после выполнения ассемблерного кода.
  • входные параметры: универсальные регистры, инициализированные от локальных переменных x и y перед выполнением ассемблерного кода.
  • изменяемые параметры: ничего, кроме регистров ввода-вывода.

Напишите отзыв о статье "GCC Inline Assembly"

Примечания

  1. [ru.wikibooks.org/wiki/Ассемблер_в_Linux_для_программистов_C Викиучебник: Ассемблер в Linux для программистов C]

Ссылки

  • [gcc.gnu.org/onlinedocs/gcc/C-Extensions.html Официальная документация]

Отрывок, характеризующий GCC Inline Assembly

– Ура! – кричали со всех сторон. С минуту толпа простояла на одном месте; но потом опять бросилась вперед.
Петя, сам себя не помня, стиснув зубы и зверски выкатив глаза, бросился вперед, работая локтями и крича «ура!», как будто он готов был и себя и всех убить в эту минуту, но с боков его лезли точно такие же зверские лица с такими же криками «ура!».
«Так вот что такое государь! – думал Петя. – Нет, нельзя мне самому подать ему прошение, это слишком смело!Несмотря на то, он все так же отчаянно пробивался вперед, и из за спин передних ему мелькнуло пустое пространство с устланным красным сукном ходом; но в это время толпа заколебалась назад (спереди полицейские отталкивали надвинувшихся слишком близко к шествию; государь проходил из дворца в Успенский собор), и Петя неожиданно получил в бок такой удар по ребрам и так был придавлен, что вдруг в глазах его все помутилось и он потерял сознание. Когда он пришел в себя, какое то духовное лицо, с пучком седевших волос назади, в потертой синей рясе, вероятно, дьячок, одной рукой держал его под мышку, другой охранял от напиравшей толпы.
– Барчонка задавили! – говорил дьячок. – Что ж так!.. легче… задавили, задавили!
Государь прошел в Успенский собор. Толпа опять разровнялась, и дьячок вывел Петю, бледного и не дышащего, к царь пушке. Несколько лиц пожалели Петю, и вдруг вся толпа обратилась к нему, и уже вокруг него произошла давка. Те, которые стояли ближе, услуживали ему, расстегивали его сюртучок, усаживали на возвышение пушки и укоряли кого то, – тех, кто раздавил его.
– Этак до смерти раздавить можно. Что же это! Душегубство делать! Вишь, сердечный, как скатерть белый стал, – говорили голоса.
Петя скоро опомнился, краска вернулась ему в лицо, боль прошла, и за эту временную неприятность он получил место на пушке, с которой он надеялся увидать долженствующего пройти назад государя. Петя уже не думал теперь о подаче прошения. Уже только ему бы увидать его – и то он бы считал себя счастливым!
Во время службы в Успенском соборе – соединенного молебствия по случаю приезда государя и благодарственной молитвы за заключение мира с турками – толпа пораспространилась; появились покрикивающие продавцы квасу, пряников, мака, до которого был особенно охотник Петя, и послышались обыкновенные разговоры. Одна купчиха показывала свою разорванную шаль и сообщала, как дорого она была куплена; другая говорила, что нынче все шелковые материи дороги стали. Дьячок, спаситель Пети, разговаривал с чиновником о том, кто и кто служит нынче с преосвященным. Дьячок несколько раз повторял слово соборне, которого не понимал Петя. Два молодые мещанина шутили с дворовыми девушками, грызущими орехи. Все эти разговоры, в особенности шуточки с девушками, для Пети в его возрасте имевшие особенную привлекательность, все эти разговоры теперь не занимали Петю; ou сидел на своем возвышении пушки, все так же волнуясь при мысли о государе и о своей любви к нему. Совпадение чувства боли и страха, когда его сдавили, с чувством восторга еще более усилило в нем сознание важности этой минуты.
Вдруг с набережной послышались пушечные выстрелы (это стреляли в ознаменование мира с турками), и толпа стремительно бросилась к набережной – смотреть, как стреляют. Петя тоже хотел бежать туда, но дьячок, взявший под свое покровительство барчонка, не пустил его. Еще продолжались выстрелы, когда из Успенского собора выбежали офицеры, генералы, камергеры, потом уже не так поспешно вышли еще другие, опять снялись шапки с голов, и те, которые убежали смотреть пушки, бежали назад. Наконец вышли еще четверо мужчин в мундирах и лентах из дверей собора. «Ура! Ура! – опять закричала толпа.
– Который? Который? – плачущим голосом спрашивал вокруг себя Петя, но никто не отвечал ему; все были слишком увлечены, и Петя, выбрав одного из этих четырех лиц, которого он из за слез, выступивших ему от радости на глаза, не мог ясно разглядеть, сосредоточил на него весь свой восторг, хотя это был не государь, закричал «ура!неистовым голосом и решил, что завтра же, чего бы это ему ни стоило, он будет военным.
Толпа побежала за государем, проводила его до дворца и стала расходиться. Было уже поздно, и Петя ничего не ел, и пот лил с него градом; но он не уходил домой и вместе с уменьшившейся, но еще довольно большой толпой стоял перед дворцом, во время обеда государя, глядя в окна дворца, ожидая еще чего то и завидуя одинаково и сановникам, подъезжавшим к крыльцу – к обеду государя, и камер лакеям, служившим за столом и мелькавшим в окнах.
За обедом государя Валуев сказал, оглянувшись в окно:
– Народ все еще надеется увидать ваше величество.
Обед уже кончился, государь встал и, доедая бисквит, вышел на балкон. Народ, с Петей в середине, бросился к балкону.
– Ангел, отец! Ура, батюшка!.. – кричали народ и Петя, и опять бабы и некоторые мужчины послабее, в том числе и Петя, заплакали от счастия. Довольно большой обломок бисквита, который держал в руке государь, отломившись, упал на перилы балкона, с перил на землю. Ближе всех стоявший кучер в поддевке бросился к этому кусочку бисквита и схватил его. Некоторые из толпы бросились к кучеру. Заметив это, государь велел подать себе тарелку бисквитов и стал кидать бисквиты с балкона. Глаза Пети налились кровью, опасность быть задавленным еще более возбуждала его, он бросился на бисквиты. Он не знал зачем, но нужно было взять один бисквит из рук царя, и нужно было не поддаться. Он бросился и сбил с ног старушку, ловившую бисквит. Но старушка не считала себя побежденною, хотя и лежала на земле (старушка ловила бисквиты и не попадала руками). Петя коленкой отбил ее руку, схватил бисквит и, как будто боясь опоздать, опять закричал «ура!», уже охриплым голосом.
Государь ушел, и после этого большая часть народа стала расходиться.
– Вот я говорил, что еще подождать – так и вышло, – с разных сторон радостно говорили в народе.
Как ни счастлив был Петя, но ему все таки грустно было идти домой и знать, что все наслаждение этого дня кончилось. Из Кремля Петя пошел не домой, а к своему товарищу Оболенскому, которому было пятнадцать лет и который тоже поступал в полк. Вернувшись домой, он решительно и твердо объявил, что ежели его не пустят, то он убежит. И на другой день, хотя и не совсем еще сдавшись, но граф Илья Андреич поехал узнавать, как бы пристроить Петю куда нибудь побезопаснее.


15 го числа утром, на третий день после этого, у Слободского дворца стояло бесчисленное количество экипажей.
Залы были полны. В первой были дворяне в мундирах, во второй купцы с медалями, в бородах и синих кафтанах. По зале Дворянского собрания шел гул и движение. У одного большого стола, под портретом государя, сидели на стульях с высокими спинками важнейшие вельможи; но большинство дворян ходило по зале.
Все дворяне, те самые, которых каждый день видал Пьер то в клубе, то в их домах, – все были в мундирах, кто в екатерининских, кто в павловских, кто в новых александровских, кто в общем дворянском, и этот общий характер мундира придавал что то странное и фантастическое этим старым и молодым, самым разнообразным и знакомым лицам. Особенно поразительны были старики, подслеповатые, беззубые, плешивые, оплывшие желтым жиром или сморщенные, худые. Они большей частью сидели на местах и молчали, и ежели ходили и говорили, то пристроивались к кому нибудь помоложе. Так же как на лицах толпы, которую на площади видел Петя, на всех этих лицах была поразительна черта противоположности: общего ожидания чего то торжественного и обыкновенного, вчерашнего – бостонной партии, Петрушки повара, здоровья Зинаиды Дмитриевны и т. п.