Я думаю, что проблема, с которой вы столкнулись, заключается в концепции «абсолютного ПК», которая на самом деле не вещь.Ваши варианты «ПК относительный» и «абсолютный».RISC-V определяет две инструкции адресации, которые позволяют эффективно реализовать эти режимы:
lui
(Load Upper Immediate): это устанавливает rd
в 32-битное значение с младшими 12 битами0 и старшие 20 битов, поступающие из непосредственного U-типа. auipc
(Добавить Upper Immediate to Program Counter): это устанавливает rd
в сумму текущего ПК и 32-битногозначение с младшими 12 битами в качестве 0 и старшими 20 битами, поступающими из непосредственного U-типа.
Эти инструкции по сути одинаковы: они оба принимают непосредственный U-тип (т. е. старший20 бит из 32-битного количества), добавьте его к чему-либо и получите результат в rd
Разница в том, что lui
добавляет это мгновенное значение к 0
, а auipc
добавляет это немедленное к ПК.Иногда легче представить два режима адресации как «относительный к ПК» и «относительный к 0», поскольку это делает различие более явным.
Хотя оба auipc
и lui
разработанычтобы работать в качестве первой инструкции в паре из двух инструкций, вторая инструкция не особенно актуальна.И auipc
, и lui
заполняют старшие 20 бит 32-битного адреса, оставляя инструкцию, с которой они связаны, чтобы заполнить младшие 12 бит.Инструкции в формате I и S спроектированы так, чтобы хорошо сочетаться здесь, и есть вариант I или S каждой инструкции в базовом ISA, для которого такой формат имел бы смысл.
В качестве конкретного примера, следующий Cкод выполняет очень простой
int global;
int func(void) { return global; }
В качестве примера, давайте предположим, что global имеет значение 0x20000004, а ПК первой инструкции в func имеет значение 0x10000008.
При компиляции с -mcmodel=medlow
(0-относительный режим адресации), вы получите
func:
lui a0, 0x20000
lw a0, 0x008(a0)
Как видите, полный абсолютный адрес global (0x2000004) заполняется в паре команд.С другой стороны, при компиляции с -mcmodel=medany
(режим относительной к ПК) вы получите
func:
auipc a0, 0x10000
lw a0, 0x004(a0)
На этот раз только смещение между ПК auipc
и целевым символомпоявляется в паре команд.Это происходит потому, что ПК явно (с помощью инструкции auipc
) включен в расчет адресации.В этом случае auipc
устанавливает a0
на 0x2000004
: вычисление выполняется a0 = PC + (imm20 << 12)
, и здесь у нас есть 0x10000004
для ПК и 0x10000
для imm20
.
Эти относящиеся к ПК последовательности адресации также допускают некоторую независимость от позиции: если вы очень осторожны в ограничении своих действий, могут быть созданы связанные двоичные файлы, которые все равно будут работать при загрузке с другим смещением по сравнению с тем, где они связаны.,На практике этого недостаточно для полной позиционно-независимой адресации в системах в стиле POSIX (именно поэтому у нас также есть аргумент -fPIC
, как и у всех остальных), но если вы находитесь в жестко ограниченной встроенной системе, вы можетебыть в состоянии сойти с рук.
Для окончательной морщины, как и почти все остальное в ISA RISC-V, немедленные значения, используемые auipc
и lui
, являются знаками, расширенными до XLEN.В 32-разрядных системах эти режимы адресации могут генерировать любой адрес в системе, но это не относится к 64-разрядным системам.Вот почему мы называем эти режимы адресации «medany» и «medlow»: «med» означает «средний», что означает окно 4 ГБ, в которое должны помещаться все глобальные символы.«Низкая» часть означает, что это окно центрировано вокруг абсолютного адреса 0, в то время как в «любой» части это окно центрировано вокруг любого ПК, с которым он связан.