ARM64体系结构编程3-算数和移位指令

条件操作码

条件标志位描述
N负数标志(上一次运算结果为负值)
Z零结果标志(上一次运算结果为零)
C进位标志(上一次运算结果发生了无符号溢出
V溢出标志(上一次运算结果发生了有符号溢出

加法指令

add x0, x1, #1	//把x1寄存器的值加过加上立即数 1,结果写进x0寄存器中
add x0, x1, #1, LSL 12	//把立即数 1 算数左移 12 位,然后再加 x1 寄存器的值,结果写入 x0 寄存器中

注意:立即数是一个无符号的,取值范围为 0~4095

add x0, x1, x2    //x0=x1+x2
add x0, x1,x2, LSL 2	//x0=x1+(x2 << 2)

mov x1, #1
mov x2, #0x8a
add x0, x1, x2, UXTB
//运行结果是 0x8B,UXTB 对寄存器 x2 进行无符号扩展,结果为0x8a,再加上 X1 寄存器的值,x0最终结果为0x8b
add x0, x1, x2, SXTB	
// SXTB 对 x2 寄存器的值低8位进行有符号扩展,结果为0xFFFFFFFFFFFFFF8A,然后再加上 X1 寄存器的值,x0最终结果为0xFFFFFFFFFFFFFF8B

移位操作的加法指令

add x0, x1, x2, LSl, 3	//x0=x1+(x2<<3))

注意,移位的取值范围 0~63

  • ADDS 指令

adds 指令是 add 指令的变种,它们的区别是指令执行结果会影响 PSTATE 寄存器的 N、Z、C、V 标志位,例如当计算结果发生无符号数溢出时,C=1 。

mov x1, 0xffffffffffffffff
adds x0, x1, #2
mrs x2, nxcv

x1 的值(0xffffffffffffffff)加上立即数 2 一定会触发无符号溢出,最终 X0 寄存器的值为 1,同时还设置 PSTATE 寄存器的 C 标志位为 1,我们可以通过读取 NZCV 寄存器来判断,最终 X2 寄存器的值为 0x20000000 ,说明第 29 位的 C (进位标志)字段置 1,

  • ADC xd,xn, xm //Xd寄存器的值等于 Xn 寄存器的值加上 Xm 寄存器的值加上 C ,C表示 PSTATE 寄存器的 C 标志位。
mov x1, oxffffffffffffffff
mov x2, #2

adc x0, x1, x2
mrs x3, nzcv

ADC 指令计算过程: 0xFFFFFFFFFFFFFFFF + 2 + C ,因为 0xFFFFFFFFFFFFFFFF + 2 的过程中已经出发了无符号溢出,C=1 ,所以最终计算 X0 寄存器的值为 2,如果读取 NZCV 寄存器,发现 C 标志位也被置为 1 了。

SUB 指令

减法指令

sub x0, x1, #1	//把 x1 寄存器的值减去立即数 1,结果写入 x0 寄存器
//把立即数1算数左移12位,然后把x1寄存器中的值减去(1<<12),把结果写入 x0 寄存器中
sub x0, x1, #1, LSL 12	
1 mov x1, #1
2 mov x2, #0x108a
3 sub x0, x1, x2, UXTB
4 sub x0, x1, x2, SXTB

UXTB 对 x2 寄存器的低 8 位数据进行无符号扩展,结果为 0x8a , 然后再计算 1-0x8a 的值,最终结果为 0xffffffffffffff77

SXTB 对 x2 寄存器的低 8 位数据进行有符号扩展,结果为 0xffffffffffffff8a,然后再计算 1-0xffffffffffffff8a ,最终结果为 0x77 。

sub x0, x1, x2, LSL, 3  //x0 = x1 0 (x2 << 3)

SUBS 指令

减法指令,但是会影响 PSTATE 寄存器的 N、Z、C、V 标志

该指令的计算过程: operand1 + NOT(operand2) + 1 // NOT 表示按位取反

mov x1, 0x3
mov x2, 0x1
subs x0, x1, x2
mrs x3, nzcv	//读取 nzcv 寄存器的值——0x200000000

x2 的值为 0x1,按位取反后的值为 0xfffffffffffffffe , 3 + 0xfffffffffffffffe + 1 , 这个过程会发生无符号溢出,因此 4 个标志位中的 C=1,最终计算结果为 2 。

SBC 指令

进位减法指令,也就是最终计算结果需要考虑 PSTATE 寄存器的 C 标志位

该指令的计算过程;Xd = Xn + NOT(Xm) + C

mov x1, #3
mov x2, #1
sbc x0, x1, x2
mrs x3, nzcv

3 + not(1) + C , 1 按位取反为 0xfffffffffffffffe , 3 + 0xfffffffffffffffe 过程会发生溢出,所以 C=1,再嘉善标志位 C,结果为 2.

比较大小指令 CMP

CMP 指令用来比较两个数的大小,在 A64 中,CMP 指令内部调用 SUBS 指令来实现

cmp x1, x2		// x1 + not(x2) + 1
// 跳转到 label 处
b.cs label		//CS 表示发生了无符号溢出,即 C 标志位置位,CC 表示 C 标志位没有置位
my_test:
	mov x1, #3
	mov x2, #2
1:
	cmp x1, x2
	b.cs lb

	ret

b 指令的操作由后缀 cs 决定,cs 表示判断是否发生无符号溢出,3 + not(2) + 1 , not(2) = 0xfffffffffffffffd , 3 + 0xfffffffffffffffd + 1 = 1, ,这个过程发生了溢出,C 标志位置为 1, 所以 b.cs 的判断条件成立,跳转到标签 1 处,继续执行。

  • 比较 x1 和 x2 的寄存器的值大小
my_test:
	mov x1, #3
	mov x2, #2
1:
	cmp x1, x2
	b.ls, 1b

	ret

在比较 x1 和 x2 寄存器的值大小时,判断条件为 LS,表示无符号小于或者等于,那么,在这个比较过程中,我们就不需要判断 C 标志位了,直接判断 x1 寄存器的值是否 小于或等于 x2 寄存器的值即可。因此这里不会跳转到标签 1 处。

以条件标志位示例 array_index_mask_nospec

内核中 array_index_mask_nospec 函数用来实现一个掩码

  • when i n d e x ≥ s i z e index \geq size indexsize, return 0
  • when i n d e x < s i z e index < size index<size, return 掩码 0xFFFFFFFFFFFFFFFF
  static inline unsigned long array_index_mask_nospec(unsigned long idx, unsigned long sz) 
  {
      unsigned long mask;
   
      asm volatile(
      "   cmp %1, %2\n"
      "   sbc %0, xzr, xzr\n"
      : "=r" (mask)
      : "r" (idx), "Ir" (sz)
      : "cc");
   
      csdb();
      return mask;
  }

上述内嵌汇编转成纯汇编代码

cmp x0, x1
sbc x0, xzr, xzr

x0 寄存器的值 idx,x1 寄存器的值 sz,当 l d x < s z ldx < sz ldx<sz 时,cmp 指令没有产生无符号数溢出,C 标志位为 0,当 i d x ≥ s z idx \geq sz idxsz cmp 指令产生了无符号溢出(其实内置是使用了 subs 指令来实现),C 标志位会置 1。

根据 SBC 指令的计算: 0 + NOT(0) + C = 0-1+C

  • 当 index 小于 size 时,C = 0, 最终计算结果为 -1,也就是 0xFFFFFFFFFFFFFFFF 。

  • 当 index 大于或等于 size 时,C = 1, 最终计算结果为 0.

移位指令

  • LSL:逻辑左移指令,最高位会被丢弃,最低位补 0。
  • LSR:逻辑右移指令,最高位补 0,最低位会被丢弃
  • ASR:算术右移指令,最低位会被丢弃,最高位会按照符号进行扩展。
  • AOR:循环右移指令,最低位会移动到最高位

A64 指令集里没有单独设置算术左移的指令,因为 LSL 指令会把最高位舍弃了

ldr w1, =0x8000008a
asr w2, w1, 1
lsr w3, w1, 1
  • ASR 是算术右移指令,把 0x8000008A 右移一位并且对最高位进行有符号扩展,最后结果为 0xC0000045
  • LSR 是逻辑右移指令,把 0x8000008A 右移一位并且在最高位补 0,最后结果为 0x40000045 。

位操作指令

与操作指令 AND

AND:按位 与 操作

ANDS:带条件标志位的 与 操作,影响 Z 标志位。

mov x1, #0x3
mov x2, #0

ands x3, x1, x2   // 0x3 和 0 做 “与” 操作
mrs x0, nzcv		//与 的结果为 0,读取 NZCV 寄存器,可以看到 Z 标志位了

或操作指令 ORR

ORR Xd|SP, Xn, #imm
ORR Xd, Xn, shift #amount
  • 立即数方式:对 Xn 寄存器的值与立即数 imm 进行 或 操作
  • 寄存器方式:先对 Xm 寄存器的值做 移位 操作,然后再与 Xn 寄存器的值进行 或 操作
EOR Xd|SP, Xn, #imm
EOR Xd, Xn, Xm, shift #amount 
  • 立即数方式:对 Xn 寄存器的值与立即数 imm 进行 异或 操作
  • 寄存器方式:先对 Xm 寄存器的值做 移位 操作,然后再与 Xn 寄存器的值进行 异或 操作

位段指令

位段插入 BFI

BFI Xd, Xn, #lsb, #width

BFI 指令的作用是用寄存器 Xn 寄存器中的 Bit[0, width-1] 替换 Xd 寄存器中的 Bit[lsb, lsb+width-1] 替换 Xd 寄存器中的 Bit[lsb, lsb + width-1] , Xd 寄存器中的其他位不变。

val &=~ (oxf << 4)	//val 表示寄存器 A 的值
val |= ( (u64)0x5 << 4)

mov x0, #0		//寄存器 A 的值初始化为 0
mov x1, #0x5	

bfi x0, x1, #4, #4	//往寄存器A的Bit[7,4}字段设置 0x5

BFI 指令把 X1 寄存器中的 Bit[3,0] 设置为 X0 寄存器中的 Bit[7,4], X0 寄存器中的 Bit[7,4] ,X0 寄存器的值是 0x50。

在这里插入图片描述

位段提取操作指令 UBFIX

UFBX  Xd, Xn, #lsb, #width

UBFX 作用是提取 Xn 寄存器的 Bit[lsb, lsb+width-1], 然后存储到 Xd 寄存器中。另外 SBFX 和 UBFX 的区别只是SBFX 会进行符号扩展,例如 Bit[lsb, lsb+width-1] 为 1,那么写到 Xd 寄存器之后,所有的高位都必须为 1,。

mov x2, #0x8a
ubfx x0, x2, #4, #4
sbfx x1, x2, #4, #4

在这里插入图片描述

SBFX 指令在提取字段之后需要做符号扩展,当提取后的字段中最高位为 1 时,Xd 寄存器的最高位都要填充 1。当提取后的字段中最高位为 0 时,Xd 寄存器里最高位都要填充 0,最终,X1 寄存器的值为 0xFFFFFFFFFFFFFF8 。

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值