AArch64教程第三章

AArch64教程第三章

在上一章,我们可以看到指令可能有寄存器操作码和立即数作为操作数。我们也要提醒大家注意,混合32位和64位寄存器是不允许的。今天我们将讲一下寄存器操作数。

寄存器操作数的操作者

很多把一个指令寄存器作为第二个操作数也同样能应用一些源寄存器的值的额外的操作。这也能作为一种方式来提高计算的密度,其方式是通过较少的指令和允许一些通用的使用一个操作数的操作。例如,转换,我们能区分两种类型的操作:移动和扩展。

移动性操作

在AArch64中有三种移动性操作:LSL,LSR,ASR和ROR。其语法如下:

reg, LSL, #amount
reg, LSR, #amount
reg, ASR, #amount
reg, ROR, #amount

其中reg可以是64位寄存器Xn或者32位寄存器Wn,amount是一个数,其范围依赖于寄存器,对于32位寄存器而言,是从0到31,64位寄存器是0到63。

LSL针对寄存器中的值执行一次逻辑左移,但它不会改变寄存器中的内容。左移n位的意思是把原始值从最低位引入n个零并抛弃最高位的n位。左移n位等价于乘以2^n

add r1, r2, r3, LSL #4 /* r1 ← r2 + (r3 << 4)
                          this is the same as
                          r1 ← r2 + r3 * 16 */
add r0, r0, r0, LSL #2  /* r0  ← r0 + r0 << 2
                           this is the same as
                           r0 ← r0 + r0 * 4
                           which happens to be the same as
                           r0 ← r0 * 5
                           assuming no overflow happens

LSR执行一次逻辑右移。该操作与LSL相对,但是0被引入到最高位并且n个最低位被抛弃。对无符号算数数字,该操作等价于除以2^n

ASR执行一次算术右移。这有点像LSR,但不同于在最高位引入n个0,最高位会在n个最高位重复n次。如同在LSR中,n个最低位被抛弃了。如果寄存器的最高位是0,ASR就等价于LSR。这种移动操作对补码数特别有用,因为它在寄存器中增加了位数(如果被解释为二进制数的话,这些位都是在最高位)并且它能够用于2的n次的负数除法。一个LSR在补码上的应用是不能用于除法目的。

操作符ROR执行一个寄存器的循环右移操作。这通常用于密码并且它的用途比其他的移位操作没那么广泛。一个循环移位与LSR类似但是LSR会抛弃位和引入0,而ROR是抛弃最低位并在最高位引入抛弃的位。没有循环左移,因为只需要把我们想左移的步骤在循环右移中写成符号即可。

在AArch64中,只有少数的指令(主要是逻辑指令)能够用循环右移操作符。

mov w2, #0x1234            // w2 ← 0x1234
mov w1, wzr                // w1 ← 0
orr w0, w1, w2, ROR #4     // w0 ← BitwiseOr(w1, RotateRight(w2, 4))
                           // this sets w0 to 0x40000123
orr w0, w1, w2, ROR #28    // w0 ← BitwiseOr(w1, RotateRight(w2, 32-4))
                           // this is in practice like RotateLeft(w2, 4)
                           // so this sets w0 to 0x12340

扩展操作符

扩展操作符的主要目的是扩展在寄存器中过窄的值,从而匹配位的数目。一个扩展操作符以kxtw的形式,其中k是一个整型值,该值是我们想要扩展的宽度。w是窄的哪个值。对于前者,整型的类型能够使U(unsigned)或者S(signed,例如,补码)。对于后者,宽度能够使B,H或者W,分别对应byte(寄存器的低8位),half-word(寄存器的低16位)或者word(寄存器的低32位)。

这也意味着扩展符有uxtb, sxtb, uxth, sxth, uxtw, sxtw

这些操作符存在时移位有时我们必须扩展源代码的值的范围,使之从一个小的宽度到一个大的宽度。后面的章节,我们会看到很多这样的例子。例如,可能我们需要增加一个32位的寄存器到一个64位的寄存器。如果两个寄存器都要表达补码。

add x0, x1, w2, sxtw  // x0 ← x1 + ExtendSigned32To64(w2)

当我们用这些扩展操作符的时候,有一些背景我们需要考虑。例如,下面两个指令有一点不同。

add x0, x1, w2, sxtb // x0 ← x1 + ExtendSigned8To64(w2)
add w0, w1, w2, sxtb // w0 ← w1 + ExtendSigned8To32(w2)

在这两个例子中,w2的低8位被扩展了,但是,在第一个例子中,它们都被扩展到了64位,但是在第二个例子中只扩展到了32位。

扩展和移位

扩展一个值并移1,2,3,4位时可行的,只要通过在扩展操作符后指定个数。例如

mov x0, #0                // x0 ← 0
mov x1, #0x1234           // x0 ← 0x1234
add x2, x0, x1, sxtw #1   // x2 ← x0 + (ExtendSigned16To64(x1) << 1)
                          // this sets x2 to 0x2468
add x2, x0, x1, sxtw #2   // x2 ← x0 + (ExtendSigned16To64(x1) << 2)
                          // this sets x2 to 0x48d0
add x2, x0, x1, sxtw #3   // x2 ← x0 + (ExtendSigned16To64(x1) << 3)
                          // this sets x2 to 0x91a0
add x2, x0, x1, sxtw #4   // x2 ← x0 + (ExtendSigned16To64(x1) << 4)
                          // this sets x2 to 0x12340

这看起来有点奇怪和随意,但是在后面的章节,我们会看到这实际上在很多情况下都是有用的。

今天就到这里。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值