OSDev Wiki
Регистрация
Advertisement

Общие сведения[]

С точки зрения прикладного программиста, процессор располагает 16-ю 32-разрядными регистрами общего назначения (РОН, GPR), из которых три на деле имеют специальные функции:

  • общие регистры R0–R12;
  • указатель стека SP, он же регистр R13;
  • регистр связи LR, он же регистр R14;
  • счётчик команд PC, он же регистр R15.

Первые 13 регистров с точки зрения архитектуры являются абсолютно равноправными, за исключением двух моментов:

  • в командах набора ARM LDRD и STRD, оперирующих сразу парой регистров, указывается всегда регистр с чётным номером, а пару с ним составляет следующий за ним регистр с нечётным номером (заметим, что в аналогичных командах набора Thumb-2 это правило не действует: оба используемых регистра указываются в них явным образом);
  • в почти всех командах набора Thumb и в некоторых командах Thumb-2 могут использоваться регистры R0–R7, но не могут регистры с большими номерами.

Остальные три регистра имеют специальное назначение. Счётчик команд как РОН использоваться не может, а в командах применяется для достижения специальных целей, причём возможности его использования зависят от вида команды и от используемой системы команд. LR может использоваться как РОН в тех случаях, когда он не применяется по своему прямому назначению. Возможность такого использования для SP определяется версией архитектуры и используемой системой команд. В частности, в архитектуре ARMv7-M при записи в SP младшие два бита принудительно устанавливаются в 0, что ограничивает возможности его применения как обычного РОНа, а документация на ARMv6-M рекомендует всегда записывать в эти два бита нули во избежание проблем с совместимостью. Подробнее об использовании этих регистров сказано ниже.

Как уже отмечалось, в системе команд Thumb свободно могут использоваться лишь регистры R0–R7, называемые также младшими регистрами. Явный доступ к старшим регистрам в Thumb крайне ограничен и возможен лишь в некоторых командах. Система команд Thumb-2 преодолела это ограничение, однако для доступа к старшим регистрам в ней применяются новые 32-разрядные команды. Таким образом, при оптимизации кода, написанного для системы команд Thumb-2, надо избегать использования старших регистров, чтобы по возможности использовались 16-разрядные команды набора Thumb. Система команд ARM ограничений на доступ к регистрам не имеет.

С точки зрения системного программиста, процессор имеет большее число регистров общего назначения, хотя в каждый конкретный момент времени доступны лишь 16 из них. В процессорах A- и R-профилей их имеется либо 31, либо 33 в зависимости от того, реализован режим монитора или нет. Это число получается следующим образом:

  • регистры R0–R7 и PC существуют в единственном экземпляре, общем для всех режимов;
  • регистры R8–R12 существуют в двух экземплярах: для режима обработки быстрого прерывания (FIQ) и для всех остальных режимов;
  • регистры LR и SP являются общими для режимов пользователя и системы, но существуют отдельно для каждого другого режима, т. е. в сумме их имеется 6 или 7 пар.

Когда необходимо указать, регистр какого именно режима используется, в документации используется запись вида LR_svc (в данном случае она указывает на регистр LR режима супервизора).

В процессорах M-профиля имеется 17 или 19 регистров, причём R0–R12, LR и PC всегда существуют в одном экземпляре, и лишь SP имеет несколько экземпляров:

  • процессор без расширения безопасности (в том числе любой процессор версии ARMv6-M или ARMv7-M) имеет два регистра: основного стека MSP и стека процесса PSP (иногда обозначаются как SP_main и SP_process);
  • процессор с расширением безопасности имеет четыре указателя стека: два для безопасного (MSP_S и PSP_S) и два для небезопасного (MSP_NS и PSP_NS) режимов.

В режиме обработчика всегда используется указатель основного стека; в режиме потока может использоваться как он, так и указатель стека процесса, что определяется содержимым управляющего регистра CONTROL (в случае наличия расширения безопасности используется регистр, соответствующий текущему режиму безопасности). Для доступа к конкретному указателю стека привилегированный код может воспользоваться командами MRS и MSR.

Особые случаи использования регистров[]

Счётчик команд PC (R15)[]

Регистр PC (Program Counter) используется для хранения адресов выполняющихся команд. При последовательном выполнении команд он последовательно увеличивается, при переходах и прерываниях в него загружается новое значение.

Многие команды запрещают программисту явно использовать PC или налагают на это определённые ограничения, что всегда оговаривается в описаниях конкретных команд. Нарушение этих правил обычно ведёт к непредсказуемым результатам.

В тех случаях, когда описание команды не налагает ограничений на использование PC, применяются изложенные ниже правила.

Считывание счётчика команд[]

Считывание PC используется главным образом для позиционно-независимой адресации, включая позиционно-независимые переходы, загрузку констант (так называемых литералов; на языке ассемблера эти операции задаются командами вида LDR Rn, =const) и вычисление адресов других команд и ячеек памяти (для этого обычно используется псевдокоманда ADR, транслируемая в зависимости от значения смещения в команду ADD или SUB).

При использовании системы команд ARM считанное значение счётчика команд будет на 8 превышать адрес текущей команды (в которой осуществляется это считывание; значение 8 обусловлено особенностями конвейера, использовавшегося в ранних версиях архитектуры, и сейчас сохраняется для совместимости). Два младших бита PC всегда будут равны нулю, поскольку команды ARM всегда выровнены на границу слова.

Исключением из этого правила является сохранение PC с помощью команд STR и STM: сохранённое в памяти значение счётчика команд будет превышать адрес команды записи либо на 8, либо на 12 в зависимости от реализации. Если одна из них сохраняет значение PC, увеличенное на 8 или 12, то и другая будет сохранять значение, увеличенное на такую же величину. Заметим, что использование в команде STR счётчика команд в роли источника адреса, по которому будет производиться сохранение, подчиняется общему правилу: его содержимое на момент вычисления адреса записи будет на 8 больше адреса самой команды.

При считывании регистра PC в командах набора Thumb его значение само по себе будет превышать адрес текущей команды на 4, при этом младший бит будет равен нулю. Однако если PC используется в 16-разрядном варианте команды ADD Rd, PC, #const, предназначенной для вычисления адреса относительно текущего значения счётчика команд, у складываемого с константой значения PC принудительно обнуляется разряд 1. В результате значение PC, выровненное на границу слова, будет на 2 или 4 превышать адрес этой команды ADD в зависимости от её собственного адреса. Аналогичному принудительному выравниванию подвергается значение PC, используемое в качестве базового адреса в 16-разрядной команде LDR Rd, [PC, #imm8 * 4], что необходимо для корректной загрузки слова (из-за необходимости выравнивания его на границу слова).

В системе команд Thumb-2, помимо двух приведённых выше случаев, принудительное выравнивание значения PC на границу слова выполняется при его использовании в качестве базового регистра в 32-разрядных командах LDC, LDR, LDRB, LDRD, LDRH, LDRSB и LDRSH, а также при использовании для вычисления адреса в 32-разрядных вариантах команд ADD Rd, PC, #const и SUB Rd, PC, #const.

Запись в счётчик команд[]

Поскольку счётчик команд содержит адрес выполняемой команды, запись в него вызывает переход на команду по записываемому адресу. В командах, где он может явно указываться в качестве регистра-приёмника, используются следующие общие правила.

В арифметико-логических командах набора ARM использование PC в качестве приёмника вызывает переход на соответствующий адрес. В ранних версиях (все варианты ARMv5 и раньше) записываемое в PC значение должно иметь нули в двух младших разрядах, иначе последствия будут непредсказуемыми. В версии ARMv6 два младших бита записываемого значения игнорируются и принудительно устанавливаются в 0. Наконец, в версии ARMv7 младший бит используется в качестве индикатора системы команд, активизируемой после перехода: если он равен нулю, по-прежнему будет использоваться система команд ARM (при этом бит 1 должен быть равен нулю), а если равен единице — система команд Thumb (тогда бит 1 является частью адреса перехода).

Специальным случаем является использование регистра PC в качестве приёмника в S-разновидностях команд обработки данных (т. е. в командах, меняющих по результатам своего выполнения состояние флагов регистра текущего состояния программы CPSR): в такой ситуации происходит возврат из прерывания, для чего в PC заносится результат операции, а в CPSR — содержимое регистра сохранённого состояния SPSR, при этом система команд устанавливается в соответствии со значением, загружаемым из SPSR. Подробнее об этом говорится в разделе «Обработка прерываний в A- и R-профилях». Заметим, что этот вариант загрузки PC может использоваться только при нахождении процессора в одном из режимов обработки прерываний, но не в режимах пользователя или системы, не имеющих SPSR.

Для перехода на заданный адрес могут использоваться и команды загрузки LDR и LDM. В версии ARMv4 два младших бита загружаемого адреса должны быть равны нулю, в противном случае результат будет непредсказуем. Начиная с версии ARMv5T, младший бит определяет систему команд, а бит 1 должен быть равен нулю, если загружаемый адрес задаёт систему команд ARM. Благодаря этому во всех версиях, начиная с ARMv5T, подобная загрузка может использоваться для возврата из подпрограммы независимо от того, какую систему команд использовала вызывающая программа (в версии ARMv4T переключение между системами команд возможно лишь с помощью команды BX, а также при возврате из прерывания).

В системе команд Thumb явная запись в PC возможна в 16-разрядных командах ADD и MOV, при этом младший бит записываемого значения игнорируется и принудительно устанавливается в 0. Переключение системы команд при этом не производится.

Загрузка PC из памяти возможна с помощью команды POP. В версии ARMv4T она игнорирует значение младшего бита загружаемого значения, принудительно устанавливая его в 0, а начиная с версии ARMv5T, использует его как индикатор системы команд, на которую выполняется переход.

В системе команд Thumb-2 возможность загрузки PC появилась и в команде LDR. Младший бит загружаемого значения рассматривается как индикатор системы команды. В отличие от системы команд ARM, в целом аналогичных командах обработки данных Thumb-2 использование PC как приёмника результата невозможно.

Указатель стека SP (R13)[]

Регистр SP предназначен для использования в качестве указателя стека.

Единственной командой набора ARM, неявно использующей SP по прямому назначению, является весьма специфическая команда SRS, доступная только в привилегированных режимах и появившаяся в ARMv6. В остальном же никаких специальных команд для работы со стеком у набора ARM нет; вместо них применяются обычные команды загрузки и записи данных, в которых SP указывается в качестве базового регистра. Благодаря этому возможна организация стека, растущего вверх или вниз, причём SP может указывать как на последнее использованное в стеке слово, так и на первое ещё не использованное. Более того, технически в качестве указателя стека можно использовать любой из регистров общего назначения, кроме PC. Это облегчается тем обстоятельством, что прерывания и вызовы подпрограмм не используют стек, сохраняя адрес возврата в регистре LR, а состояние прерванной программы — в регистре SPSR.

В системе команд Thumb, напротив, имеются специальные команды POP и PUSH, использующие SP как указатель стека. Стек растёт вниз, а значение SP указывает на последнее помещённое в него слово. Заметим, что в унифицированном языке ассемблера для системы команд ARM поддерживаются аналогичные мнемоники, хотя они транслируются в определённые разновидности команд LDM и STM.

Использование SP в 16-разрядных командах обработки данных очень ограничено. В 32-разрядных командах во многих случаях применение SP в качестве приёмника результата запрещено (в аналогичных командах набора ARM он может использоваться свободно).

Когда SP используется по прямому назначению, он должен содержать значение, кратное 4, поскольку обращение к стеку выполняется словами. В некоторых версиях архитектуры, например, в ARMv7-M, при записи в SP два младших разряда принудительно сбрасываются. Однако, если нужна переносимость кода между разными версиями архитектуры, программист должен убедиться, что помещаемые в SP значения никогда не будут содержать единиц в двух младших битах.

Регистр связи LR (R14)[]

Регистр LR (Link Register) используется в следующих случаях:

  • в командах BL, BLX и BLXNS;
  • при выполнении прерываний.

Перечисленные выше команды применяются для вызова подпрограмм, при этом в LR помещается адрес команды, следующей за BL или BLX, либо код возврата из функции (FNC_RETURN) для команды BLXNS.

В случае возникновения прерывания в процессорах A- и R-профилей в LR заносится значение PC, на 4 или 8 превосходящее адрес команды, выполняемой непосредственно перед прерыванием; подробнее об этом говорится в разделе «Обработка прерываний в A- и R-профилях». В процессоре M-профиля при прерывании в LR заносится специальный код, называемый в документации EXC_RETURN и определяющий, как необходимо выполнять возврат из обработчика данного прерывания (см. раздел «Обработка прерываний в M-профиле)».

Стандартные соглашения о связях[]

Хотя при создании своей ОС программист волен определять собственные соглашения о связях и правила использования регистров, есть смысл придерживаться правил, разработанных компанией ARM и поддерживаемых всеми популярными трансляторами для этой архитектуры. Вкратце они заключаются в следующем.

  • В качестве указателя стека всегда используется регистр SP (R13). Стек растёт вниз, SP указывает на последнее используемое слово. Эти правила полностью соответствуют аппаратным требованиям набора команд Thumb.
  • Для передачи параметров в вызываемую подпрограмму могут использоваться регистры R0–R3. Если необходимо передать параметр, по размерам превышающий слово, при использовании обычного порядка слов «младший–старший» младшее слово помещается в регистр с меньшим номером; при использовании порядка «старший–младший» — в регистр со старшим номером. Если параметров слишком много, дополнительные параметры передаются через стек. Кроме того, при наличии арифметического сопроцессора для передачи параметров могут использоваться его регистры, но их использование здесь не обсуждается.
  • Возврат результатов осуществляется через регистры R0–R3.
  • Вызываемая подпрограмма обязана сохранять значения регистров R4–R11, а также корректно восстанавливать значение указателя стека.
  • Регистры R0–R3, R12 и LR, а также флаги регистра состояния вызываемой подпрограммой могут свободно изменяться.
  • Компоновщик при связывании вызовов подпрограмм может использовать регистр R12 в качестве рабочего, поэтому подпрограммы не могут рассчитывать на сохранность его содержимого, если используют вызовы подпрограмм из других модулей.
  • В ряде систем R7 хранит значение, зависящее от системы (например, адрес локальной памяти потока — TLS). В таких системах изменять этот регистр обычно запрещается.

Указатель стека сам по себе всегда должен быть выровнен на границу слова. Тем не менее, в некоторых случаях требуется его выравнивание на границу двойного слова. Для контроля за соблюдением выравнивания на границу двойного слова объектные модули могут снабжаться специальными атрибутами, которые контролируются компоновщиком. Последний, в частности, не должен допускать вызова подпрограмм, расположенных в модуле, требующем выравнивания стека на границу двойного слова, из подпрограмм, относящихся к модулю, обеспечивающему выравнивание лишь на границу слова. Компиляторы языков высокого уровня обычно снабжают свои файлы соответствующими атрибутами сами; при программировании на языке ассемблера это является задачей программиста. Например, транслятор ассемблера фирмы KEIL имеет для этого директивы PRESERVE8 (модуль обеспечивает выравнивание стека на границу двойного слова, если он уже был на неё выровнен) и REQUIRE8 (модуль требует выравнивания стека на границу двойного слова).

Advertisement