Программирование на языке Assembler
Псевдо-инструкции
Компилятор языка Assembler транслирует программу в машинный код. В рамках курса в основном рассматривается GNU assembler. Компилятор понимает не только инструкции RISC-V, определяемые стандартом ISA, но также понимает псевдо-инструкции и псевдо-директивы.
Псевдо-инструкции являются частью языка Assembler и могут быть странслированы в машинный код. Однако, в отличие от инструкций из набора ISA, псевдо-инструкции гораздо проще использовать. Обычно псевдо-инструкция может быть странслирована в более чем одну базовую инструкцию набора ISA. Кроме того, псевдо-инструкции решают проблему использования функций ручного перемещения памяти. В следующих таблицах показаны псевдо-инструкции и то, в какие базовые инструкции они преобразуются.
Псевдо-инструкции для загрузки, сохранения и дополнения
Псевдо-инструкция | Набор базовых инструкций | Описание |
---|---|---|
la rd, symbol | auipc rd, symbol[31:12] addi rd, symbol[11:0] | Загрузить адрес (не позиционно-независимый код - non-PIC) |
la rd, symbol | auipc rd, symbol@GOT[31:12] l{w|d} rd, symbol[11:0](rd) | Загрузить адрес (позиционно-независимый код - PIC) |
lla ra, symbol | auipc rd, symbol[31:12] addi rd, rd, symbol[11:0] | Загрузить локальный адрес |
lga rd, symbol | auipc rd, symbol@GOT[31:12] l{w|d} rd, symbol@GOT[11:0](rd) | Загрузить глобальный адрес |
l{b|h|w|d} rd, symbol | auipc rd, symbol[31:12] l{b|h|w|d} rd, symbol[11:0](rd) | Загрузить глобальное значение |
s{b|h|w|d} rs, symbol, rd | auipc rd, symbol[31:12] s{b|h|w|d} rs, symbol[11:0](rd) | Сохранить глобальное значение |
nop | addi x0, x0, 0 | Нет операции |
li rd, imm | Различные варианты инструкций | Загрузить непосредственное значение |
mv rd, rs | addi rd, rs, 0 | Скопировать значение регистра |
not rd, rs | xori rd, rs, -1 | дополнение до 1 (инверсия битов) |
neg rd, rs | sub rd, x0, rs | дополнение до 2 (инверсия битов с добавлением 1) |
negw rd, rs | subw rd, x0, rs | дополнение до 2 (слово) |
Положение позиционно-независимого кода (Position Independent Code, PIC) в адресном пространстве зависит только от настроек компоновщика (а не от архитектуры) и полезно для генерации бинарного кода библиотек, которые могут использоваться другими программами. Такой код обычно используется совместно с глобальной таблицей смещений (Global Offset Table, GOT), которая хранится в исполняемом модуле и позволяет операционной системе загружать библиотеки при старте вызывающих программ по различным адресам памяти. Тема, связанная с этим вопросом, является достаточно сложной и не будет рассмотрена в рамках данного курса.
Для самостоятельно изучения вопросов, связанных с форматом ELF, можно посоветовать ознакомиться с документом RISC-V ELF Specification, доступным по адресу https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc.
Отдельно стоит отметить, что псевдо-инструкция lga не поддерживается в наборе инструментов GNU toolchain версии 2.39 и ниже.
Рассмотрим на примере следующего кода, как он транслируется с точки зрения компилятора языка Assembler:
.text .globl _start _start: la x1, counter #load address of counter addi x1, x1, 4 #go to next word address += 4 lw x2, 0(x1) #load value from address, the 2 li x1, 2 lw x3, counter #load word from address counter, the 0 add x3, x2, x1 #add: x3 = x2 + x1 sw x3, counter, x2 #save x3, use x2 for address .data counter: .word 0, 2 Disassembly of section .text: 00000000000100e8 <_start>: 100e8: 00001097 auipc x1,0x1 100ec: 02808093 addi x1,x1,40 # 11110 <__DATA_BEGIN__> 100f0: 00408093 addi x1,x1,4 100f4: 0000a103 lw x2,0(x1) 100f8: 00200093 addi x1,x0,2 100fc: 00001197 auipc x3,0x1 10100: 0141a183 lw x3,20(x3) # 11110 <__DATA_BEGIN__> 10104: 001101b3 add x3,x2,x1 10108: 00001117 auipc x2,0x1 1010c: 00312423 sw x3,8(x2) # 11110 <__DATA_BEGIN__>
Адрес метки counter в памяти равен 0b11110. Псевдо-инструкции транслируются в базовые инструкции следующим образом:
la x1, counter -> auipc x1,0x1; addi x1, x1, 40 li x1, 2 -> addi x1, x0, 2 lw x3, counter -> auipc x3, 0x1; lw x3, 20(x3) sw x3, counter, x2 -> auipc x2, 0x1; sw x3, 8(x2)
Псевдо-инструкции для условной установки битов
Таблица 4.3 ниже показывает псевдо-инструкции и базовые инструкции для расширения и условной установки битов:
Псевдо-инструкция | Набор базовых инструкций | Описание |
---|---|---|
sext.{b|h|w} rd, rs | Может быть различный набор инструкций | sign extend |
zext.{b|h|w} rd, rs | Может быть различный набор инструкций | zero extend |
seqz rd, rs | sltiu rd, rs, 1 | rd = (rs == 0)? 1:0 |
snez rd, rs | sltu rd, x0, rs | rd = (rs != 0)? 1:0 |
sltz rd, rs | slt rd, rs, x0 | rd = (rs < 0)? 1:0 |
sgtz rd, rs | slt rd, x0, rs | rd = (rs > 0)? 1:0 |