6.47.5.2 Определение регистров для локальных переменных
Вы можете определить локальную переменную-регистр и связать ее с указанным регистром так:
register int *foo asm ("r12");
Здесь r12 — это имя регистра, который должен использоваться. Обратите внимание, что этот синтаксис аналогичен синтаксису определения глобальных переменных-регистров, но для локальной переменной объявление находится внутри функции. Ключевое слово register обязательно и не может быть комбинировано с static. Имя регистра должно быть допустимым именем регистра для целевой платформы.
Не используйте квалификаторы типа, такие как const и volatile, так как результат может быть не таким, как ожидалось. В частности, когда используется квалификатор const, компилятор может заменить переменную её инициализатором в выражениях asm, что может привести к появлению соответствующего операнда в другом регистре.
Как и в случае с глобальными переменными-регистрами, рекомендуется выбирать регистр, который обычно сохраняется и восстанавливается вызовами функций на вашей машине, чтобы вызовы библиотечных процедур не перезаписывали его.
Единственное поддерживаемое использование этой функции — указание регистров для операндов ввода и вывода при вызове Расширенной asm (см. Расширенная ассемблерная инструкция с операндами выражений C). Это может быть необходимо, если ограничения для определенной машины не обеспечивают достаточного управления для выбора желаемого регистра. Чтобы принудительно поместить операнд в регистр, создайте локальную переменную и укажите имя регистра после объявления переменной. Затем используйте локальную переменную для операнда 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-13.3.0/gcc/Local-Register-Variables.html