пятница, 28 мая 2021 г.

Краткое руководство по ассемблеру Go: константы и символы

Константы

Хотя ассемблер руководствуется ассемблерами Plan 9, это отдельная программа, поэтому есть некоторые отличия. Первое, что он находится в постоянной оценке. Константные выражения в ассемблере анализируются с использованием приоритета операторов Go, а не C-подобного приоритета оригинала. Таким образом, 3&1<<2 равно 4, а не 0 - он анализирует как (3&1)<<2, а не 3&(1<<2). Кроме того, константы всегда оцениваются как 64-разрядные целые числа без знака. Таким образом, -2 - это не целое число минус два, а 64-битное целое число без знака с тем же битовым шаблоном. Различие редко имеет значение, но во избежание двусмысленности деление или сдвиг вправо, когда установлен старший бит правого операнда, отклоняется.

Символы

Некоторые символы, такие как R1 или LR, предопределены и относятся к регистрам. Точный набор зависит от архитектуры.

Есть четыре заранее объявленных символа, которые относятся к псевдорегистрам. Это не настоящие регистры, а скорее виртуальные регистры, поддерживаемые цепочкой инструментов, такие как указатель кадра (frame pointer). Набор псевдорегистров одинаков для всех архитектур:

  • FP: (Frame pointer) Указатель кадра: аргументы и локальные переменные.
  • PC: (Program counter) Счетчик программы: прыжки и ветки.
  • SB: (Static base pointer) Статический базовый указатель: глобальные символы.
  • SP: (Stack pointer) Указатель стека: верх стека.

Все определяемые пользователем символы записываются как смещения в псевдорегистры FP (аргументы и локальные переменные) и SB (глобальные переменные).

Псевдорегистр SB можно рассматривать как источник памяти, поэтому символ foo(SB) - это имя foo как адрес в памяти. Эта форма используется для именования глобальных функций и данных. Добавление <> к имени, как в foo<>(SB), делает имя видимым только в текущем исходном файле, как статическое объявление верхнего уровня в файле C. Добавление смещения к имени относится к этому смещению от адреса символа, поэтому foo+4(SB) находится на четыре байта после начала foo.

Псевдо-регистр FP - это указатель виртуального кадра, используемый для ссылки на аргументы функции. Компиляторы поддерживают указатель виртуального кадра и ссылаются на аргументы в стеке как на смещения от этого псевдорегистра. Таким образом, 0(FP) - это первый аргумент функции, 8(FP) - второй (на 64-битной машине) и так далее. Однако при таком обращении к аргументу функции необходимо поместить имя в начало, как в first_arg+0(FP) и second_arg+8(FP). (Значение смещения - смещение от указателя кадра - отличается от его использования с SB, где это смещение от символа.) Ассемблер обеспечивает соблюдение этого соглашения, отклоняя простые 0(FP) и 8(FP). Фактическое имя семантически не имеет значения, но должно использоваться для документирования имени аргумента. Стоит подчеркнуть, что FP всегда является псевдорегистром, а не аппаратным регистром, даже на архитектурах с аппаратным указателем кадра.

Для функций сборки с прототипами Go go vet проверит совпадение имен аргументов и смещений. В 32-битных системах младшие и старшие 32 бита 64-битного значения различаются добавлением суффикса _lo или _hi к имени, как в arg_lo+0(FP) или arg_hi+4(FP). Если прототип Go не называет свой результат, ожидаемое имя сборки - ret.

Псевдо-регистр SP - это указатель виртуального стека, используемый для ссылки на локальные переменные кадра и аргументы, подготавливаемые для вызовов функций. Он указывает на верхнюю часть кадра локального стека, поэтому ссылки должны использовать отрицательные смещения в диапазоне [−framesize, 0): x-8(SP), y-4(SP) и так далее.

В архитектурах с аппаратным регистром SP префикс имени отличает ссылки на указатель виртуального стека от ссылок на архитектурный регистр SP. То есть x-8(SP) и -8(SP) являются разными ячейками памяти: первая относится к псевдорегистру виртуального указателя стека, а вторая - к регистру SP оборудования.

На машинах, где SP и PC традиционно являются псевдонимами физического нумерованного регистра, в ассемблере Go имена SP и PC по-прежнему обрабатываются особым образом; например, ссылки на SP требуют символа, как и FP. Для доступа к фактическому регистру оборудования используйте истинное имя R. Например, в архитектуре ARM аппаратный SP и PC доступны как R13 и R15.

Ответвления и прямые переходы всегда записываются как смещения к PC или как переходы к меткам:

label:
	MOVW $0, R1
	JMP label

Каждая метка видна только в той функции, в которой она определена. Поэтому для нескольких функций в файле разрешено определять и использовать одни и те же имена меток. Прямые переходы и инструкции вызова могут нацеливаться на текстовые символы, такие как name(SB), но не на смещения от символов, такие как name+4(SB).

Инструкции, регистры и директивы ассемблера всегда пишутся ВЕРХНИМ РЕГИСТРОМ, чтобы напомнить вам, что программирование на ассемблере - это рискованное занятие. (Исключение: переименование g регистра на ARM.)

В объектных файлах и двоичных файлах Go полное имя символа - это путь к пакету, за которым следует точка и имя символа: fmt.Printf или math/rand.Int. Поскольку синтаксический анализатор ассемблера обрабатывает точку и косую черту как знаки препинания, эти строки нельзя использовать непосредственно как имена идентификаторов. Вместо этого ассемблер разрешает использование символа средней точки U+00B7 и разделительной косой черты U+2215 в идентификаторах и заменяет их на точку и косую черту. В исходном файле ассемблера указанные выше символы записываются как fmt·Printf и math∕rand·Int. Списки сборок, сгенерированные компиляторами при использовании флага -S, показывают точку и косую черту непосредственно вместо замены Unicode, требуемой ассемблерами.

Большинство рукописных файлов сборки не включают полный путь к пакету в именах символов, потому что компоновщик вставляет путь к пакету текущего объектного файла в начало любого имени, начинающегося с точки: в исходном файле сборки в math/rand реализации пакета, функция пакета Int может называться ·Int. Это соглашение позволяет избежать необходимости жесткого кодирования пути импорта пакета в его собственном исходном коде, что упрощает перенос кода из одного места в другое.


Читайте также:


Комментариев нет:

Отправить комментарий