6.47.5.2 Указание регистров для локальных переменных ¶
Вы можете определить локальную переменную регистра и связать её с указанным регистром так:
register int *foo asm ("r12");
Здесь r12 — имя регистра, который следует использовать. Обратите внимание, что это тот же синтаксис, что и для определения глобальных переменных регистров, но для локальной переменной объявление появляется внутри функции. Ключевое слово register обязательно и не может быть комбинировано с static. Имя регистра должно быть допустимым именем регистра для целевой платформы.
Не используйте квалификаторы типа, такие как const и volatile, так как результат может быть непредсказуемым. В частности, когда используется квалификатор const, компилятор может заменить переменную её инициализатором в операторах asm, что может привести к появлению соответствующего операнда в другом регистре.
Как и в случае с глобальными переменными регистров, рекомендуется выбирать регистр, который обычно сохраняется и восстанавливается вызовами функций на вашей машине, чтобы вызовы библиотечных процедур не изменяли его содержимое.
Единственное поддерживаемое использование этой функции — указание регистров для операндов ввода и вывода при вызове расширенного asm (см. Расширенный ассемблерный код — ассемблерные инструкции с операндами выражений языка Си). Это может потребоваться, если ограничения для определённой машины не обеспечивают достаточного управления для выбора нужного регистра. Чтобы поместить операнд в регистр, создайте локальную переменную и укажите имя регистра после объявления переменной. Затем используйте локальную переменную для операнда asm и укажите любой символ ограничения, соответствующий регистру:
register int *p1 asm ("r0") = …;
register int *p2 asm ("r1") = …;
register int *result asm ("r0");
asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2));
Предупреждение: В приведенном выше примере обратите внимание, что регистр (например, r0) может быть изменён вызывающей стороной последующим кодом, включая вызовы функций и библиотечных функций для арифметических операций над другими переменными (например, инициализация p2). В этом случае используйте временные переменные для выражений между назначениями регистров:
int t1 = …;
register int *p1 asm ("r0") = …;
register int *p2 asm ("r1") = t1;
register int *result asm ("r0");
asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2));
Определение переменной регистра не резервирует регистр. Помимо вызова расширенного asm, содержимое указанного регистра не гарантируется. По этой причине следующие использования явным образом не поддерживаются. Если они кажутся работоспособными, это просто случайность, и они могут перестать работать по задумке из-за (кажущихся) не связанных изменений в окружающем коде или даже незначительных изменений в оптимизации будущей версии gcc:
- Передача параметров в или из базового
asm - Передача параметров в или из расширенного
asmбез использования операндов ввода или вывода. - Передача параметров в или из процедур, написанных на языке ассемблера (или других языках), с использованием нестандартных соглашений о вызовах.
Некоторые разработчики используют локальные переменные регистров, пытаясь улучшить распределение регистров gcc, особенно в больших функциях. В этом случае имя регистра по существу является подсказкой для распределителя регистров. Хотя в некоторых случаях это может привести к более эффективному коду, улучшения зависят от прихотей распределителя/оптимизаторов. Поскольку нет гарантии, что ваши улучшения не будут потеряны, использование локальных переменных регистров не рекомендуется.
На платформе MIPS есть связанное использование локальных переменных регистров с несколько отличающимися характеристиками (см. Определение особенностей сопроцессоров для целей MIPS в GNU Compiler Collection (GCC) Internals).
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-11.4.0/gcc/Local-Register-Variables.html