ARMv8 Cortex-a 编程向导手册学习_3.ARMv8 指令集介绍

/* TODO 本系列文章是对 ARMv8 Cortex-a 系列编程向导手册拙劣的翻译和注解,若有出入,以官方文档为准 */

Chapter 5 ARMv8 指令集介绍

ARMv8 架构中最重要的一个改变是引入了一个 64 位的指令集,该 64 位指令集对现有的 32 位指令集做出了补充,并可以提供 64 位宽的寄存器、内存以及指针的访问。这个 64 位的指令集被称为 A64 ,该指令集只能在 AArch64 执行状态下使用。
ARMv8 也提供了 32 位的指令集: A32 与 T32,该指令集实现了对 ARMv7 的兼容,这两个指令集只能在 AArch32 状态下使用。
注意:A64 指令集的指令码依然是 32 位宽的。

5.1 ARMv8 指令集

A64 指令集与 A32 指令集存在一定的相似性,比如都是 32 宽的,而且语法也相似。
在 AArch32 状态下,core 使用如下指令集:

  • A32 :兼容已经存在的 ARMv7 指令集。
  • T32:16 位的 Thumb 指令集,同样用于兼容 ARMv7 指令集。

在 AArch64 状态下,core 使用全新的指令集:

  • A64:A64 指令集提供的功能与 A32 与 T32 拥有一定的相似性

相比较于 ARMv7 指令集,A64 拥有如下的重大改进:

  • 一致的编码机制,在 A32 中某些指令的编码机制于主流的编码机制并不相同,比如 LDR 与 STR 指令对半字的编码,就与字节和字的编码不同。
  • 范围更宽的立即数,比如算数指令通常接受 12 位的立即数;逻辑指令通常接受 32 位和 64 位的立即数; MOV 指令接受 16 位的立即数,这16位可以是任意的连续的16位;地址生成指令通常适用于 4KB 对齐的地址
  • 数据类型更简单,A64 处理有符号数与无符号数更自然
  • 长偏移。A64指令通常提供更长的便偏移,比如跳转指令。
  • 64 位宽的指针类型,AArch64 状态下,指针类型都是64 位的
  • A64 的代码生成更具有规律性,这是因为 AArch64 的通用目的寄存器不需要区分高寄存器或低寄存器
  • A64 指令集都是固定的 32 位长的。

5.1.1 区分操作32位整数与操作64位整数的 A64 指令

大多数 A64 指令可以操作两种数据宽度:用于操作 32 位的值或 64 位的值。
通过下列方式,可以区分 A64 指令操作的数据宽度:

  • 查看指令所使用的寄存器名字,比如通用目的寄存器使用 Xn, 那么这是64位的值,如果使用 Wn,那么这是 32 位的值

当指令操作数是 32 位时,存在如下特征:

  • 右移和取反都是限制于 bit31, 而不是 bit63
  • 指令根据低32位的值来设置条件标志位
  • 写 Wn 寄存器会设置对应的 Xn 寄存器的 bit[63:32] 为0

5.1.2 地址

AArch64 可以存储64位的值,处理器可以更简单的访问超过 4GB 的地址。AArch64 有如下提升:

  • 互斥访问:字节,半字,字,双字的互斥访问。
  • 增加 PC 相关的跳转偏移
  • 非对齐的地址支持
  • 块传输,在AArch64 状态下,A64 指令不再支持 LDM, STM, PUSH, POP 这些块传输指令,所有的块传输都通过 LDP 与 STP 指令实现。
  • 对齐检查

5.1.3 寄存器

A64 的 64 位寄存器组减少了大多数应用中的寄存器压力。
A64 的流程调用标准(PCS)可以在函数调用时使用 X0-X7 传递多达8个的形参。而 A32 只能传递 4 个参数。
A64 的 PCS 也定义了一个专用的帧指针 FP,这可以让调试与代码分析更容易,因为它可以可靠的转开堆栈。
AArch64 状态下,每一种数据类型都有标准的类型宽度,通常使用 LP64, 即 long 类型与指针类型都是 64 位的。

AArch64 提供了如下的特殊寄存器

寄存器名 寄存器描述
WZR/XZR 0寄存器,读为0,写无效
SP 栈寄存器,栈寄存器可以使用算数相关的指令和 MOV 指令访问,其他大多数指令不能访问
PC 程序计数器,A64 没有提供直接访问 PC 寄存器的指令,只提供了间接访问 PC 的指令,比如跳转指令,异常生成与异常返回,pc 永远指向当前正在操作的指令,而没有 A32 中的偏移

5.2 C/C++ 内嵌汇编

下面介绍如何在 C 或 C++ 程序中引用汇编代码。
使用 asm 关键字可以在 C 程序中包含 GCC 汇编语句,如下:

#include 
int add(int i, int j)
{
int res = 0;
asm (
"ADD %w[result], %w[input_i], %w[input_j]" //Use `%w[name]` to operate on W
// registers (as in this case).
// You can use `%x[name]` for X
// registers too, but this is the
// default.
: [result] "=r" (res)
: [input_i] "r" (i), [input_j] "r" (j)
);
return res;
}
int main(void)
{
int a = 1;
int b = 2;
int c = 0;
c = add(a,b)
printf(“Result of %d + %d = %d\n, a, b, c);
}

上述是规范所列举的例子,实际中,我通常使用如下的内嵌汇编格式:

__asm__ __volatile__("msr TTBR0_EL1, %0"::"r"(ulAddr));	/* 设置L1翻译表地址 */
__asm__ __volatile__("mrs %0, CurrentEL ":"=r"(ulVal));	/* 获取当前异常等级 */

5.3 指令集的切换

在同一个应用程序中,不可能使用两种指令集,类比两种执行状态,要么是 AArch32 执行状态,要么是AArch64 执行状态。
A64 指令代码不能在 ARMv7 处理器上运行,但是 ARMv7 的代码可以在 ARMv8 的 AArch32 状态下运行,如下图:

ARMv8 Cortex-a 编程向导手册学习_3.ARMv8 指令集介绍_第1张图片
指令集的切换规则与执行状态的切换规则一致。

你可能感兴趣的:(armv8,ARM,学习,arm,arm开发)