A64 指令集

Load/Store 指令

寻址模式

Base register - w0=[x1]

ldr     w0, [x1]

Offset addressing mode - w0=[x1+12]

ldr     w0, [x1, 12]

Pre-index addressing mode - x1+=12; w0=[x1]

ldr     w0, [x1, 12]!

Post-index addressing mode - w0=[x1]; x1+=12

ldr     w0, [x1], 12

更多示例

// load a byte from x1
ldrb    w0, [x1]

// load a signed byte from x1
ldrsb   w0, [x1]

// store a 32-bit word to address in x1
str     w0, [x1]

// load two 32-bit words from stack, then add 8-byte to sp
ldp     w0, w1, [sp], 8

// Store two 64-bit regs in [sp-96] and subtract 96-byte from sp.
// ==> [sp]=x1, [sp+8]=x2
stp     x1, x2, [sp, -16]!

// LDR伪指令. load 32-bit immediate from literal pool(addr: 0x12345678)
ldr     w0, =0x12345678

数据处理指令

Bitfield 操作指令

Bitfield 指令常用于设置/提取寄存器的某个字段.

;BFI(Bit Field Insert)
BFI w0, w0, #9, #6   ;w0[0, 5] = w0[9, 14]

;BFC(Bit Field Clear)
BFC w0, #4, #2       ;w0[4, 5] = 0

;UBFX(Unsigned Bit Field Extract)
UBFX w1, w0, #18, #7 ;w1=w0[18, 24]

与 UBFX 相对的是 SBFX, 若提取后的字段高位为 1, 会进行符号扩展

分支/控制指令

获得地址的相关指令

写汇编中,常常会获取一个变量或者函数(label)的地址,A64 中一共有几种获取地址的指令, 包括adr, adrp, adrl, ldr等,下面我就对这些指令进行介绍。

ADR

首先是 adr,它的作用是加载 PC 相对地址。PC相对的另一层含义就是物理地址, 而不一定是编译后指令的VMA,仅仅取决于当前的PC在哪,返回的就是偏移后的结果。

我们都知道,指令正常运行不一定PC对应的指令一等等于其在可执行文件中的VMA, 就拿bring-up代码做参考即可。这时候指令能正常执行的原因是:取指动作永远都是简单的PC+1, 仅仅与PC有关而已。

adr 指令的格式是:

adr reg, offset

输入一个 offset,输出 PC+offset 的值,但我们常用的是 offset 填某一个 label,例如函数 或者变量,此时 reg 中存的就是此函数的地址了。例如,

adr x0, a_func    # x0 = addr(a_func)

adr 指令中留给 offset 的位数是 21,所以最大的寻址范围为+-1MB。

ADRP

adrp 的指令格式与 adr 相同,不同点是会将计算的结果向下对齐到 4k,但这样直接计算的 地址结果往往是不正确的,因为少了页内的偏移。需要一个额外的 add 指令进行纠正,一般来说 是组合起来用的:

adrp x0, a_func
add  x0, x0, :lo12:a_func

显然 adrp 这样做是为了更大的寻址空间,能到+-4GB。

LDR 伪指令

一般通用的ldr指令都是用来加载内存中的值,比如常见的 ldr x0, [x1]。 或者说容易混淆的加载label处内存的值: ldr x0, label

如果 ldr 指令的参数是=<label>,那就代表伪指令,作用是加载 label 的绝对地址, 注意不是内存的值,而是地址, 拿到的地址是绝对地址,即VMA。 好处是可以获取到一个位于任意 label 的地址,不受寻址空间的约束。

简单说下ldr和ldr伪指令的区别

label_1:  // assmue VMA of label_1 = 0x12345678
  .word 0xabcdabcd

ldr x0, addr   // x0 = 0xabcdabcd
ldr x0, =addr  // x0 = 0x12345678

当目前指令执行地址不等于它们的虚拟地址时,比如当 MMU 未开始时通过 uboot 加载到内存 中直接运行的那段程序,此时用adr/adrp这些加载 pc 相对地址的指令就会正确的返回 label 的实际运行地址;而ldr则是永远返回其编译链接后绑定的虚拟地址。这种情况下 如果寻址范围允许,用adr*指令更方便。

Hint 指令

  • yield指令属于Hint指令的一种,其预留是为了通知处理器当前线程是“不重要的”,处理器可以切换到其他线程去执行。!!注意这个地方的线程指的是硬件线程,而目前所有的ARM处理器都是单硬件线程的,所以 yield 在所有ARM处理器上实现为 nop

有趣特性/常见误区

‘#’ before the immediate value

  • A64 assembly language does not require the # to introduce constant immediate value. But the assembler can also indentify the #.
  • In armv7, there must be a # or $ before other than using .syntax unified. About syntax unified.

Agreed Recommendation

Use .syntax unified in v7 code, and never use # on any literal on either v7 or v8. Unified syntax is newer and better, and those # and $ signs are just more code noise.


创建于: 2022-05-07T21:19:01, Lastmod: 2024-07-29T21:58:50