【ARMv8 编程】A64 数据处理指令——移动&比较指令

移动指令主要为 MOV 以及它的各种“变体”,而比较指令主要用来进行比较并更新条件标志,用来实现条件判断等。

指令类型 指令
移动 MOV、MVN、MOVK、MOVZ、MOVN
比较 CMP、CMN、TST

一、移动指令

1.1 MOV

MOV (to/from SP)

在寄存器和堆栈指针之间移动:Rd = Rn。该指令是 ADD(立即数)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

MOV <Wd|WSP>, <Wn|WSP>

等价指令

ADD <Wd|WSP>, <Wn|WSP>, #0

64-bit (sf == 1)

MOV <Xd|SP>, <Xn|SP>

等价指令

ADD <Xd|SP>, <Xn|SP>, #0

<Wd|WSP> 是目标通用寄存器或堆栈指针的 32 位名称,编码在“Rd”字段中。

<Wn|WSP> 是源通用寄存器或堆栈指针的 32 位名称,在“Rn”字段中编码。

<Xd|SP> 是目标通用寄存器或堆栈指针的 64 位名称,编码在“Rd”字段中。

<Xn|SP> 是源通用寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

下面是使用 MOV (to/from SP) 指令的例子。

    long long int x = 0;

    asm volatile(
        "MOV %x[x], SP\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

MOV %x[x], SP 指令执行会将 SP 的值移动到 %x[x] 寄存器中,在笔者的机器环境内,某次运行最终 x 的值为 0x7ff7745260。

我们先在 asm volatile 代码上加入断点,debug 去看 SP 寄存器的值。

在这里插入图片描述

此时使用 lldb 命令可以看到 SP 具体值如下:

在这里插入图片描述

这和我们最终运行得到的 x 值是一致的。

MOV (inverted wide immediate)

将按位取反的 16 位立即数移动到寄存器:Rd = imm。该指令是 MOVN 指令的别名。

在这里插入图片描述

32-bit (sf == 0)

MOV <Wd>, #<imm>

等价指令

MOVN <Wd>, #<imm16>, LSL #<shift>

64-bit (sf == 1)

MOV <Xd>, #<imm>

等价指令

MOVN <Xd>, #<imm16>, LSL #<shift>

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<imm> 对于 32 位变体:是一个 32 位立即数,其按位取反的值可以在“imm16:hw”中编码,但不包括 0xffff0000 和 0x0000ffff; 对于 64 位变体:是一个 64 位立即数,其按位取反的值可以在“imm16:hw”中编码。

<imm16> 是计算出的 16 位立即数,它与 <shift> 一起编码 NOT(<imm>)。

<shift> 对于 32 位变体:是计算的最小左移 0 或 16,应用于 <imm16> 将编码 NOT(<imm>) 的值;对于 64 位变体:计算的最小左移 0、16、32 或 48 应用于 <imm16> 将编码 NOT(<imm>) 的值。

下面是使用 MOV (inverted wide immediate) 指令的例子。

    long long int x = 0;

    asm volatile(
        "MOV %x[x], #0xFFFFFFFFFFFFFFF7\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

我们将 8(0x0000000000000008) 按位取反就会得到 0xFFFFFFFFFFFFFFF7,接着调用 MOV %x[x], #0xFFFFFFFFFFFFFFF7 指令就会将 0xFFFFFFFFFFFFFFF7 移动到 %x[x] 使用的寄存器,也就是最终 x 的值为 -9(0xFFFFFFFFFFFFFFF7)。

MOV (wide immediate)

将 16 位立即数移动到寄存器:Rd = imm。该指令是 MOVZ 指令的别名。

在这里插入图片描述

32-bit (sf == 0)

MOV <Wd>, #<imm>

等价指令

MOVZ <Wd>, #<imm16>, LSL #<shift>

64-bit (sf == 1)

MOV <Xd>, #<imm>

等价指令

MOVZ <Xd>, #<imm16>, LSL #<shift>

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<imm> 对于 32 位变体:是一个 32 位立即数,可以在“imm16:hw”中编码;对于 64 位变体:是一个 64 位立即数,可以在“imm16:hw”中编码。

<imm16> 是计算出的 16 位立即数,它与 <shift> 一起编码 <imm>

<shift> 对于 32 位变体:是计算的最小左移 0 或 16,应用于 <imm16> 将编码 <imm> 的值;对于 64 位变体:计算的最小左移 0、16、32 或 48 应用于 <imm16> 将编码 <imm> 的值。

下面是使用 MOV (wide immediate) 指令的例子。

    long long int x = 0;

    asm volatile(
        "MOV %x[x], #8\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 MOV %x[x], #8,我们将 8 移动到 %x[x] 使用的寄存器,也就是最终 x 的值为 8。

MOV (bitmask immediate)

将位掩码立即数移动到寄存器:Rd = imm。该指令是 ORR(立即数)指令的别名。

在这里插入图片描述

32-bit (sf = 0, N = 0)

MOV <Wd|WSP>, #<imm>

等价指令

ORR <Wd|WSP>, WZR, #<imm>

64-bit (sf == 1)

MOV <Xd|SP>, #<imm>

等价指令

ORR <Xd|SP>, XZR, #<imm>

<Wd|WSP> 是目标通用寄存器或堆栈指针的 32 位名称,在“Rd”字段中编码。

<Xd|SP> 是目标通用寄存器或堆栈指针的 64 位名称,在“Rd”字段中编码。

<imm> 是位掩码立即数,以“N:imms:immr”编码,但不包括可以由 MOVZ 或 MOVN 编码的值。

下面是使用 ORR <Xd|SP>, XZR, #<imm> 指令的例子,和 MOV <Xd|SP>, #<imm> 是等价的,单单从 MOV 立即数指令来看是很难区分出到底是 MOV (inverted wide immediate),还是 MOV (wide immediate),或者是 MOV (bitmask immediate)。

    long long int x = 0;

    asm volatile(
        "ORR %x[x], XZR, #8\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 ORR %x[x], XZR, #8,我们将 8 和 XZR(零值)寄存器进行按位或运算,最后将结果移动到 %x[x] 使用的寄存器,也就是最终 x 的值还为 8。

MOV (register)

将寄存器的值移动到其他寄存器:Rd = Rm。该指令是 ORR(移位寄存器)指令的别名。

在这里插入图片描述

32-bit (sf = 0)

MOV <Wd>, <Wm>

等价指令

ORR <Wd>, WZR, <Wm>

64-bit (sf == 1)

MOV <Xd>, <Xm>

等价指令

ORR <Xd>, XZR, <Xm>

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Wm> 是通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<Xm> 是通用源寄存器的 64 位名称,在“Rm”字段中编码。

下面是使用 MOV (register) 指令的例子。

    long long int x = 0;
    long long int y = 8;

    asm volatile(
        "MOV %x[x], %x[y]\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 MOV %x[x], %x[y],我们将 %x[y](即 8) 移动到 %x[x] 使用的寄存器,也就是最终 x 的值为 8。

1.2 MOVK

Move wide with keep 将一个可选移位的 16 位立即值移动到一个寄存器中,保持其他位不变。

在这里插入图片描述

32-bit (sf == 0 && hw == 0x)

MOVK <Wd>, #<imm>{, LSL #<shift>}

64-bit (sf == 1)

MOVK <Xd>, #<imm>{, LSL #<shift>}

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<imm> 是 16 位无符号立即数,范围为 0 到 65535,在“imm16”字段中编码。

<shift> 对于 32 位变体:是立即向左移动的量,0(默认值)或 16,在“hw”字段中编码为 <shift>/16;对于 64 位变体:是向左移动的量,可以是 0(默认值)、16、32 或 48,在“hw”字段中编码为 <shift>/16

下面是使用 MOVK 指令的例子。

    long long int x = 0;
    long long int y = -1;

    asm volatile(
        "MOV %x[x], %x[y]\n"
        "MOVK %x[x], #0xf5b7, LSL#16\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 MOV %x[x], %x[y],我们将 %x[y](0xFFFF FFFF FFFF FFFF(-1 的 64 位补码形式)) 的值移动到 %x[x],接着执行 MOVK %x[x], #0xf5b7, LSL#16,先将 0xf5b7 逻辑左移 16 位,然后移动到 %x[x] 寄存器,由于要保持 %x[x] 寄存器内其他位不变,因此其内部值变为 0xFFFF FFFF F5B7 FFFF,注意 f5b7 的位置,它是直接替换 %x[x] 寄存器对应的位!这也是最终 x 的值。

1.3 MOVN

MOVN(Move wide with NOT)指令将可选移位的 16 位立即数的按位取反值移动到寄存器。该指令由别名 MOV(inverted wide immediate)使用。

在这里插入图片描述

32-bit (sf == 0 && hw == 0x)

MOVN <Wd>, #<imm>{, LSL #<shift>}

64-bit (sf == 1)

MOVN <Xd>, #<imm>{, LSL #<shift>}

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<imm> 是 16 位无符号立即数,范围为 0 到 65535,在“imm16”字段中编码。

<shift> 对于 32 位变体:是立即向左移动的量,0(默认值)或 16,在“hw”字段中编码为 <shift>/16;对于 64 位变体:是向左移动的量,可以是 0(默认值)、16、32 或 48,在“hw”字段中编码为 <shift>/16

下面是使用 MOVN 指令的例子。

    long long int x = 0xff;

    asm volatile(
        "MOVN %x[x], #0x80ff, LSL#16\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 MOVN %x[x], #0x80ff, LSL#16,将 0x80ff 逻辑左移 16 位,即得到 0x80FF0000,然后按位取反 0xFFFF FFFF 7F00 FFFF,把这个值移动到寄存器 %x[x],这也是最终 x 的值。不难看出这里的值完全覆盖了 %x[x] 寄存器的初始值 0xFF。

1.4 MOVZ

MOVZ(Move wide with zero)指令将一个可选移位的 16 位立即值移动到一个寄存器。 该指令由别名 MOV(wide immediate)使用。

在这里插入图片描述

32-bit (sf == 0 && hw == 0x)

MOVZ <Wd>, #<imm>{, LSL #<shift>}

64-bit (sf == 1)

MOVZ <Xd>, #<imm>{, LSL #<shift>}

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<imm> 是 16 位无符号立即数,范围为 0 到 65535,在“imm16”字段中编码。

<shift> 对于 32 位变体:是立即向左移动的量,0(默认值)或 16,在“hw”字段中编码为 <shift>/16;对于 64 位变体:是向左移动的量,可以是 0(默认值)、16、32 或 48,在“hw”字段中编码为 <shift>/16

下面是使用 MOVZ 指令的例子。

    long long int x = 0xff;

    asm volatile(
        "MOVZ %x[x], #0x80ff, LSL#16\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 MOVZ %x[x], #0x80ff, LSL#16,将 0x80ff 逻辑左移 16 位,即得到 0x80FF0000,把这个值移动到寄存器 %x[x],这也是最终 x 的值。不难看出这里的值完全覆盖了 %x[x] 寄存器的初始值 0xFF。

1.5 MVN

MVN(按位非)指令将寄存器值的按位取反写入目标寄存器。 该指令是 ORN(移位寄存器)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

MVN <Wd>, <Wm>{, <shift> #<amount>}

等价指令

ORN <Wd>, WZR, <Wm>{, <shift> #<amount>}

64-bit (sf == 1)

MVN <Xd>, <Xm>{, <shift> #<amount>}

等价指令

ORN <Xd>, XZR, <Xm>{, <shift> #<amount>}

<Wd> 是通用目标寄存器的 32 位名称,在“Rd”字段中编码。

<Wm> 是通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xd> 是通用目标寄存器的 64 位名称,在“Rd”字段中编码。

<Xm> 是通用源寄存器的 64 位名称,在“Rm”字段中编码。

<shift> 是应用于最终源的可选移位,默认为 LSL 并在“shift”字段中编码。 它可以具有以下值:

<shift> shift 字段取值
LSL shift = 00
LSR shift = 01
ASR shift = 10
ROR shift = 11

<amount> 对于 32 位变体:是移位量,范围为 0 到 31,默认为 0 并在“imm6”字段中编码。对于 64 位变体:是移位量,在 0 到 63 范围内,默认为 0 并在“imm6”字段中编码。

下面是使用 MVN 指令的例子。

    long long int x = 0xff;
    long long int y = 0x80;

    asm volatile(
        "MVN %x[x], %x[y], LSL#16\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 MVN %x[x], %x[y], LSL#16,将 %x[y] 寄存器的值逻辑左移 16 位,也就是 0x80 逻辑左移 16 位,即得到 0x800000,接着将其取反 0xFFFF FFFF FF7F FFFF,把这个值移动到寄存器 %x[x],这也是最终 x 的值。

二、比较指令

A64 指令集不支持对每条指令进行条件执行。指令的预测执行并不能提供足够的好处来证明其对操作码空间的大量使用是合理的。

条件标志见下表:

标志 名称 详细描述
N 负(Negative) 设置为与结果的位 [31] 相同的值。对于 32 位有符号整数,设置位 [31] 表示该值为负数。
Z 零(Zero) 如果结果为零则设置为 1,否则设置为 0。
C 进位(Carry) 设置为结果的进位值,或设置为从移位操作移出的最后一位的值。
V 溢出(Overflow) 如果发生有符号溢出或下溢,则设置为 1,否则设置为 0。

如果无符号运算的结果溢出结果寄存器,则设置 C 标志。V 标志的操作方式与 C 标志相同,但用于带符号的操作。

CMP 和 CMN 如果原始条件为真,则将条件标志设置为比较结果。如果不为真,则将条件标志设置为指定的条件标志状态。条件比较指令对于表达嵌套或复合比较非常有用。

条件码表如下:

Code 编码 含义(当由 CMP 设置时) 含义(当由 FCMP 设置时) 条件标志
EQ 0b0000 等于 等于 Z = 1
NE 0b0001 不等于 无序或不等于 Z = 0
CS 0b0010 进位设置(与 HS 相同) 大于、等于或无序(与 HS 相同) C = 1
HS 0b0010 大于,等于(无符号)(与 CS 相同) 大于、等于或无序(与 CS 相同) C = 1
CC 0b0011 进位清除(与 LO 相同) 小于(与 LO 相同) C = 0
LO 0b0011 无符号小于(与 CC 相同) 小于(与 CC 相同) C = 0
MI 0b0100 Minus,Negative(负的) 小于 N = 1
PL 0b0101 正数或零 大于、等于或无序 N = 0
VS 0b0110 有符号溢出 无序。(至少有一个参数是 NaN) V = 1
VC 0b0111 无符号溢出 不是无序的。(没有参数是 NaN) V = 0
HI 0b1000 大于(无符号) 大于或无序 (C = 1) && (Z = 0)
LS 0b1001 小于或等于(无符号) 小于或等于 (C = 0) || (Z = 1)
GE 0b1010 大于或等于(有符号) 大于或等于 N == V
LT 0b1011 小于(有符号) 小于或无序 N != V
GT 0b1100 大于(有符号) 大于 (Z == 0) && (N == V)
LE 0b1101 小于或等于(有符号) 小于、等于或无序 (Z == 1) || (N != V)
AL 0b1110 总是执行 默认。总是执行 Any
NV 0b1111 总是执行 总是执行 Any

2.1 CMP

CMP (extended register)

比较(扩展寄存器)指令从寄存器值中减去符号或零扩展寄存器值(后跟可选的左移量)。 从 <Rm> 寄存器扩展的参数可以是字节、半字、字或双字。 它根据结果更新条件标志,并丢弃结果。 该指令是 SUBS(扩展寄存器)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMP <Wn|WSP>, <Wm>{, <extend> {#<amount>}}

等价指令

SUBS WZR, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}

64-bit (sf == 1)

CMP <Xn|SP>, <R><m>{, <extend> {#<amount>}}

等价指令

SUBS XZR, <Xn|SP>, <R><m>{, <extend> {#<amount>}}

<Wn|WSP> 是第一个源通用寄存器或堆栈指针的 32 位名称,在“Rn”字段中编码。

<Wm> 是第二个通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xn|SP> 是第一个源通用寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<R> 是宽度说明符,在“option”字段中编码。 它可以具有以下值:

R option
W 00x
W 010
X x11
W 10x
W 110

<m> 是第二个通用源寄存器的编号 [0-30] 或名称 ZR (31),编码在“Rm”字段中。

<extend> 对于 32 位变体:是应用于第二个源操作数的扩展,编码在“option”字段中。 它可以具有以下值:

extend option
UXTB 000
UXTH 001
LSL|UXTW 010
UXTX 011
SXTB 100
SXTH 101
SXTW 110
SXTX 111

如果“Rn”为“11111”(WSP)且“option”为“010”,则首选 LSL,但当“imm3”为“000”时可以省略。在所有其他情况下,<extend> 是必需的,并且当“option”为“010”时必须为 UXTW。

<extend> 对于 64 位变体:是应用于第二个源操作数的扩展,在“选项”字段中编码。 它可以具有以下值:

extend option
UXTB 000
UXTH 001
UXTW 010
LSL|UXTX 011
SXTB 100
SXTH 101
SXTW 110
SXTX 111

如果“Rn”为“11111”(SP)且“option”为“011”,则首选 LSL,但当“imm3”为“000”时可省略。在所有其他情况下,<extend> 是必需的,并且当“option”为“011”时必须是 UXTX。

<amount> 是扩展后要应用的左移量,范围为 0 到 4,默认为 0,编码在“imm3”字段中。 当 <extend> 不存在时它必须不存在,当 <extend> 是 LSL 时它是必需的,并且当 <extend> 存在但不是 LSL 时它是可选的。

下面是使用 CMP (extended register) 指令的例子。

    long long int x = 0xff;
    long long int y = 0x80;

    asm volatile(
        "CMP %x[x], %x[y], LSL#2\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 CMP %x[x], %x[y], LSL#2,将 %x[y] 寄存器的值逻辑左移 2 位,也就是 0x80 逻辑左移 2 位,即得到 0x200,%x[x] < %x[y],此时会设置条件标志,并且一定是 N!=V。

加入断点查看指令执行对条件标志的影响:

在这里插入图片描述

0x60001000(NZCV = 0b0110,也就是 Z(零标志位) 和 C(进位标志位) 都为 1)。

0x80001000(NZCV = 0b1000,也就是 N(负数标志位) 为 1)。

由此可见以上 CMP 指令执行后,的确是 N 不等于 V。

在这里插入图片描述

CMP (immediate)

CMP(立即数)指令从寄存器值中减去可选移位的立即数。它根据结果更新条件标志,并丢弃结果。该指令是 SUBS(立即数)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMP <Wn|WSP>, #<imm>{, <shift>}

等价指令

SUBS WZR, <Wn|WSP>, #<imm> {, <shift>}

64-bit (sf == 1)

CMP <Xn|SP>, #<imm>{, <shift>}

等价指令

SUBS XZR, <Xn|SP>, #<imm> {, <shift>}

<Wn|WSP> 是源通用寄存器或堆栈指针的 32 位名称,在“Rn”字段中编码。

<Xn|SP> 是源通用寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<imm> 是一个无符号立即数,范围为 0 到 4095,在“imm12”字段中编码。

<shift> 是应用于立即数的可选左移,默认为 LSL #0 并在“sh”字段中编码。当 sh = 0 时,LSL #0;当 sh = 1 时,LSL #12。

下面是使用 CMP (immediate) 指令的例子。

    long long int x = 0xff;

    asm volatile(
        "CMP %x[x], #1, LSL#12\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 CMP %x[x], #0x80, LSL#12,将 1 逻辑左移 12 位,即得到 4096,%x[x] < 4096,此时会设置条件标志,并且一定是 N!=V(N(负数标志位) 为 1,V(溢出标志位)为 0)。

CMP (shifted register)

CMP(移位寄存器)指令从寄存器值中减去可选移位的寄存器值。它根据结果更新条件标志,并丢弃结果。该指令是 SUBS(移位寄存器)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMP <Wn>, <Wm>{, <shift> #<amount>}

等价指令

SUBS WZR, <Wn>, <Wm> {, <shift> #<amount>}

64-bit (sf == 1)

CMP <Xn>, <Xm>{, <shift> #<amount>}

等价指令

SUBS XZR, <Xn>, <Xm> {, <shift> #<amount>}

<Wn> 是第一个通用源寄存器的 32 位名称,在“Rn”字段中编码。

<Wm> 是第二个通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xn> 是第一个通用源寄存器的 64 位名称,在“Rn”字段中编码。

<Xm> 是第二个通用源寄存器的 64 位名称,在“Rm”字段中编码。

<shift> 是要应用于第二个源操作数的可选移位类型,默认为 LSL 并在“shift”字段中编码。 它可以具有以下值:

<shift> “shift”编码
LSL 00
LSR 01
ASR 10
保留 11

<amount> 对于 32 位变体:是移位量,范围为 0 到 31,默认为 0 并在“imm6”字段中编码。对于 64 位变体:是移位量,范围为 0 到 63,默认为 0,编码在“imm6”字段。

下面是使用 CMP (shifted register) 指令的例子。

    long long int x = 0xff;
    long long int y = 1;

    asm volatile(
        "CMP %x[x], %x[y], LSL#12\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 CMP %x[x], %x[y], LSL#12,将 %x[y] 逻辑左移 12 位,即 1 逻辑左移 12 位,即得到 4096,255(0xFF) < 4096,即 %x[x] < %x[y],此时会设置条件标志,并且一定是 N!=V(N(负数标志位) 为 1,V(溢出标志位)为 0)。

2.2 CMN

CMN (extended register)

CMN(比较负数(Compare Negative)扩展寄存器)指令一个寄存器值和一个符号或零扩展寄存器值(后跟一个可选的左移量)相加。 从 <Rm> 寄存器扩展的参数可以是字节、半字、字或双字。它根据结果更新条件标志,并丢弃结果。该指令是ADDS(扩展寄存器)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMN <Wn|WSP>, <Wm>{, <extend> {#<amount>}}

等价指令

ADDS WZR, <Wn|WSP>, <Wm>{, <extend> {#<amount>}}

64-bit (sf == 1)

CMN <Xn|SP>, <R><m>{, <extend> {#<amount>}}

等价指令

ADDS XZR, <Xn|SP>, <R><m>{, <extend> {#<amount>}}

<Wn|WSP> 是第一个源通用寄存器或堆栈指针的 32 位名称,在“Rn”字段中编码。

<Wm> 是第二个通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xn|SP> 是第一个源通用寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<R> 是宽度说明符,在“option”字段中编码。 它可以具有以下值:

R option
W 00x
W 010
X x11
W 10x
W 110

<m> 是第二个通用源寄存器的编号 [0-30] 或名称 ZR (31),编码在“Rm”字段中。

<extend> 对于 32 位变体:是应用于第二个源操作数的扩展,编码在“option”字段中。 它可以具有以下值:

extend option
UXTB 000
UXTH 001
LSL|UXTW 010
UXTX 011
SXTB 100
SXTH 101
SXTW 110
SXTX 111

如果“Rn”为“11111”(WSP)且“option”为“010”,则首选 LSL,但当“imm3”为“000”时可以省略。在所有其他情况下,<extend> 是必需的,并且当“option”为“010”时必须为 UXTW。

<extend> 对于 64 位变体:是应用于第二个源操作数的扩展,在“选项”字段中编码。 它可以具有以下值:

extend option
UXTB 000
UXTH 001
UXTW 010
LSL|UXTX 011
SXTB 100
SXTH 101
SXTW 110
SXTX 111

如果“Rn”为“11111”(SP)且“option”为“011”,则首选 LSL,但当“imm3”为“000”时可省略。在所有其他情况下,<extend> 是必需的,并且当“option”为“011”时必须是 UXTX。

<amount> 是扩展后要应用的左移量,范围为 0 到 4,默认为 0,编码在“imm3”字段中。 当 <extend> 不存在时它必须不存在,当 <extend> 是 LSL 时它是必需的,并且当 <extend> 存在但不是 LSL 时它是可选的。

下面是使用 CMN (extended register) 指令的例子。

    long long int x = -1;
    long long int y = -1;

    asm volatile(
        "CMN %x[x], %x[y], LSL#2\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 CMN %x[x], %x[y], LSL#2,将 %x[y] 逻辑左移 2 位,即 -1(0xFFFF FFFF FFFF FFFF) 逻辑左移 2 位,即得到 0xFFFF FFFF FFFF FFFC,0xFFFF FFFF FFFF FFFF > 0xFFFF FFFF FFFF FFFC,即 %x[x] > %x[y],此时会设置条件标志,观察一下条件如何变化。

在这里插入图片描述

在这里插入图片描述

0x60001000(NZCV = 0b0110,也就是 Z(零标志位)和 C(进位标志位) 都为 1)。

0xa0001000(NZCV = 0b1010,也就是 N(负数标志位)为 1 和 C(进位标志位) 都为 1)。

说明运行过程中,清除了 Z,并且置位了 N,实际上同时也置位了 C(在 CMN %x[x], %x[y], LSL#2 指令前插入 ADDS %x[z], %x[z], XZR(假设 %x[z] 为 1),会清除所有 NZCV 标志)。

CMN (immediate)

CMN(比较负数立即数)指令设置条件标志并丢弃结果:Rn + shift(imm)。该指令是 ADDS(立即数)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMN <Wn|WSP>, #<imm>{, <shift>}

等价指令

ADDS WZR, <Wn|WSP>, #<imm> {, <shift>}

64-bit (sf == 1)

CMN <Xn|SP>, #<imm>{, <shift>}

等价指令

ADDS XZR, <Xn|SP>, #<imm> {, <shift>}

<Wn|WSP> 是源通用寄存器或堆栈指针的 32 位名称,在“Rn”字段中编码。

<Xn|SP> 是源通用寄存器或堆栈指针的 64 位名称,在“Rn”字段中编码。

<imm> 是一个无符号立即数,范围为 0 到 4095,在“imm12”字段中编码。

<shift> 是应用于立即数的可选左移,默认为 LSL #0 并在“shift”字段中编码:

<shift> “shift”编码
LSL#0 00
LSL#12 01
保留 1x

下面是使用 CMN (immediate) 指令的例子。

    long long int x = -1;

    asm volatile(
        "CMN %x[x], #-1, LSL#12\n"
    :[x] "+r"(x)
    :
    : "cc", "memory");

执行 CMN %x[x], #-1, LSL#12,将 -1(0xFFFF FFFF FFFF FFFF) 逻辑左移 12 位,即得到 0xFFFF FFFF FFFF F000,0xFFFF FFFF FFFF FFFF > 0xFFFF FFFF FFFF F000,即 -1 > -4096,此时会设置条件标志,清除了 Z,并且置位了 N,同时也置位了 C。

CMN (shifted register)

CMN(比较负值移位寄存器)指令设置条件标志并丢弃结果:Rn + shift(Rm, amount)。该指令是 ADDS(移位寄存器)指令的别名。

在这里插入图片描述

32-bit (sf == 0)

CMN <Wn>, <Wm>{, <shift> #<amount>}

等价指令

ADDS WZR, <Wn>, <Wm> {, <shift> #<amount>}

64-bit (sf == 1)

CMN <Xn>, <Xm>{, <shift> #<amount>}

等价指令

ADDS XZR, <Xn>, <Xm> {, <shift> #<amount>}

<Wn> 是第一个通用源寄存器的 32 位名称,在“Rn”字段中编码。

<Wm> 是第二个通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xn> 是第一个通用源寄存器的 64 位名称,在“Rn”字段中编码。

<Xm> 是第二个通用源寄存器的 64 位名称,在“Rm”字段中编码。

<shift> 是要应用于第二个源操作数的可选移位类型,默认为 LSL 并在“shift”字段中编码。 它可以具有以下值:

<shift> “shift”编码
LSL 00
LSR 01
ASR 10
保留 11

<amount> 对于 32 位变体:是移位量,范围为 0 到 31,默认为 0 并在“imm6”字段中编码。对于 64 位变体:是移位量,范围为 0 到 63,默认为 0,编码在“imm6”字段。

下面是使用 CMN (shifted register) 指令的例子。

    long long int x = -1;
    long long int y = -1;

    asm volatile(
        "CMN %x[x], %x[y], LSL#12\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 CMN %x[x], %x[y], LSL#12,将 %x[y] (-1(0xFFFF FFFF FFFF FFFF)) 逻辑左移 12 位,即得到 0xFFFF FFFF FFFF F000,0xFFFF FFFF FFFF FFFF > 0xFFFF FFFF FFFF F000,即 -1 > -4096,此时会设置条件标志,清除了 Z,并且置位了 N,同时也置位了 C。

2.3 TST

TST (immediate)

TST(测试位立即数),设置条件标志并丢弃结果:Rn AND imm。该指令是 ANDS(立即数)指令的别名。

在这里插入图片描述

32-bit (sf = 0, N = 0)

TST <Wn>, #<imm>

等价指令

ANDS WZR, <Wn>, #<imm>

64-bit (sf == 1)

TST <Xn>, #<imm>

等价指令

ANDS XZR, <Xn>, #<imm>

<Wn> 是通用源寄存器的 32 位名称,在“Rn”字段中编码。

<Xn> 是通用源寄存器的 64 位名称,在“Rn”字段中编码。

<imm> 是位掩码立即数,编码为“N:imms:immr”。

下面是使用 TST (immediate) 指令的例子。

    long long int x = 1;
    long long int y = 1;

    asm volatile(
        "ADDS %x[y], %x[y], XZR\n"
        "TST %x[x], #0x80\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 ADDS %x[y], %x[y], XZR 是为了清除条件标志,接着执行 TST %x[x], #0x80 相当于按位与操作,按位与结果为 0,所以会设置 Z 标志。但如果我们将 x 的值赋为 -1,则结果肯定不会是 0,所以也就不会置位 Z 标志。

TST (shifted register)

TST(测试移位寄存器)对寄存器值和可选移位的寄存器值执行按位与运算。它根据结果更新条件标志,并丢弃结果。该指令是 ANDS(移位寄存器)指令的别名。

在这里插入图片描述

32-bit (sf = 0)

TST <Wn>, <Wm>{, <shift> #<amount>}

等价指令

ANDS WZR, <Wn>, <Wm>{, <shift> #<amount>}

64-bit (sf == 1)

TST <Xn>, <Xm>{, <shift> #<amount>}

等价指令

ANDS XZR, <Xn>, <Xm>{, <shift> #<amount>}

<Wn> 是第一个通用源寄存器的 32 位名称,在“Rn”字段中编码。

<Wm> 是第二个通用源寄存器的 32 位名称,在“Rm”字段中编码。

<Xn> 是第一个通用源寄存器的 64 位名称,在“Rn”字段中编码。

<Xm> 是第二个通用源寄存器的 64 位名称,在“Rm”字段中编码。

<shift> 是应用于最终源的可选移位,默认为 LSL 并在“shift”字段中编码。 它可以具有以下值:

<shift> “shift”编码
LSL 00
LSR 01
ASR 10
ROR 11

<amount> 对于 32 位变体:是移位量,范围为 0 到 31,默认为 0 并在“imm6”字段中编码。对于 64 位变体:是移位量,在 0 到 63 范围内,默认为 0 并在“imm6”字段中编码。

下面是使用 TST (immediate) 指令的例子。

    long long int x = 1;
    long long int y = 1;

    asm volatile(
        "ADDS %x[y], %x[y], XZR\n"
        "TST %x[x], %x[y], LSL#1\n"
    :[x] "+r"(x),
     [y] "+r"(y)
    :
    : "cc", "memory");

执行 ADDS %x[y], %x[y], XZR 是为了清除条件标志,接着执行 TST %x[x], %x[y], LSL#1 相当于先将 %x[y] 也就是 1 左移 1 位(2),接着和 %x[x](1) 按位与操作,0b10 & 0b01 按位与结果为 0,所以会设置 Z 标志。但如果我们将 x 的值赋为 -1(0xFFFF FFFF FFFF FFFF),则结果肯定不会是 0,所以也就不会置位 Z 标志。

参考资料

1.《ARMv8-A-Programmer-Guide》
2.《Arm® A64 Instruction Set Architecture Armv8, for Armv8-A architecture profile》

猜你喜欢

转载自blog.csdn.net/tyyj90/article/details/130024069