6.11.6.2 Указание регистров для локальных переменных
Вы можете определить локальную переменную-регистр и связать её с указанным регистром следующим образом:
register int *foo asm ("r12");
Здесь r12 — имя регистра, который следует использовать. Обратите внимание: используется тот же синтаксис, что и при определении глобальных переменных-регистров, но для локальной переменной объявление размещается внутри функции. Ключевое слово register обязательно и не может сочетаться с static. Имя регистра должно быть допустимым именем регистра для целевой платформы.
Не используйте квалификаторы типа, такие как const и volatile, поскольку результат может оказаться неожиданным. В частности, при использовании квалификатора const компилятор может подставить вместо переменной её инициализатор в операторах asm, из-за чего соответствующий операнд может оказаться в другом регистре.
Как и в случае с глобальными переменными-регистрами, рекомендуется выбирать регистр, который на вашем компьютере обычно сохраняется и восстанавливается при вызовах функций, чтобы вызовы библиотечных подпрограмм не затирали его содержимое.
Эта возможность поддерживается только для указания регистров входных и выходных операндов при вызове расширенного 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)»).
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-15.3.0/gcc/Local-Register-Variables.html