文章目录
- 一、CCMP
-
- [1.1 CCMP (immediate)](#1.1 CCMP (immediate))
- [1.2 CCMP (register)](#1.2 CCMP (register))
- 二、CSEL
- 三、demo
-
- [3.1 demo1](#3.1 demo1)
- [3.2 demo2](#3.2 demo2)
- [3.3 demo3](#3.3 demo3)
一、CCMP
1.1 CCMP (immediate)
条件比较(CCMP)立即数指令是ARMv8架构中的一种高级条件执行指令。它根据前一个条件的结果来决定是否执行当前的比较操作,用于有条件地更新处理器的状态标志位(NZCV)。
Encoding for the 64-bit variant:
c
CCMP <Xn>, #<imm>, #<nzcv>, <cond>
<Xn>:64 位(Xn)通用寄存器
#imm:5位无符号(正)立即数,取值范围:0-31(2⁵-1)
#nzcv:一个 4 位立即数,取值范围:0-15(2⁴-1),直接指定 N、Z、C、V 的值(当条件不满足时使用)
<cond>:ARM 条件码(如 EQ、NE、LT、GE 等)

执行流程:
c
如果(条件码)为真:
执行 Rn - 立即数 的比较
将结果的条件标志(NZCV)存入 PSTATE
否则:
直接将指定的 NZCV值 存入 PSTATE
NZCV相关知识请参考:ARM64 --- NZCV标志位
举例说明:
c
CCMP W0, #10, #0b0001, NE
如果 NE 条件成立(即前一次操作结果不为零):
比较 W0 和 10,根据 W0 - 10 的结果设置 NZCV
如果 NE 不成立(即前一次结果为零):
直接设置标志位为:N=0, Z=0, C=0, V=1(因为 0b0001 表示 V=1)
备注:
条件码依赖:CCMP依赖前一条指令设置的标志位
立即数范围:立即数通常有范围限制(如0-31)
标志位设置:NZCV参数直接设置所有4个标志位
无寄存器修改:只影响标志位,不修改任何通用寄存器
1.2 CCMP (register)
条件比较(CCMP)寄存器指令是 ARMv8 架构中的另一种条件比较指令,它与立即数版本类似,但比较的是两个寄存器而非寄存器与立即数。
Encoding for the 64-bit variant:
c
CCMP <Xn>, <Xm>, #<nzcv>, <cond>
二、CSEL
三、demo
3.1 demo1
我们要实现的 C 代码是:
c
if (a != 0 && b != 0)
y = 1;
else
y = 0;
b 只有在 a != 0 时才参与判断
逻辑拆解:
c
if (a != 0 && b != 0)
等价于:
c
if (a != 0) {
if (b != 0)
true;
else
false;
} else {
false;
}
汇编代码:
c
.section .text
.global _start
_start:
// -------------------------------
// 输入
// -------------------------------
mov x0, #1 // a = 1 ← 改 0 / 1 测试
mov x1, #1 // b = 1 ← 改 0 / 1 测试
// -------------------------------
// if (a != 0 && b != 0)
// -------------------------------
cmp x0, #0 // a ? 0 → 设置 Z
ccmp x1, #0, #0b0100, ne
// ↑ ↑
// | |
// if (a!=0) else: 强制 Z=1(false)
// -------------------------------
// 用最终 NZCV 选结果
// -------------------------------
mov x2, #1 // true
mov x3, #0 // false
csel x4, x2, x3, ne
// 保存 NZCV
mrs x5, nzcv
nop
// exit
mov x8, #93
mov x0, #0
svc #0
c
$ as -o ccmp_csel_and_demo.o ccmp_csel_and_demo.s
$ ld -o ccmp_csel_and_demo ccmp_csel_and_demo.o
$ gdb ./ccmp_csel_and_demo
......
(gdb) set disassemble-next-line on
(gdb) starti
Starting program: ccmp_csel_and_demo
c
0x0000000000400078 in _start ()
=> 0x0000000000400078 <_start+0>: 20 00 80 d2 mov x0, #0x1 // #1
(gdb) si
0x000000000040007c in _start ()
=> 0x000000000040007c <_start+4>: 21 00 80 d2 mov x1, #0x1 // #1
(gdb) si
0x0000000000400080 in _start ()
=> 0x0000000000400080 <_start+8>: 1f 00 00 f1 cmp x0, #0x0
(gdb) si
0x0000000000400084 in _start ()
=> 0x0000000000400084 <_start+12>: 24 18 40 fa ccmp x1, #0x0, #0x4, ne // ne = any
(gdb) si
0x0000000000400088 in _start ()
=> 0x0000000000400088 <_start+16>: 22 00 80 d2 mov x2, #0x1 // #1
(gdb) si
0x000000000040008c in _start ()
=> 0x000000000040008c <_start+20>: 03 00 80 d2 mov x3, #0x0 // #0
(gdb) si
0x0000000000400090 in _start ()
=> 0x0000000000400090 <_start+24>: 44 10 83 9a csel x4, x2, x3, ne // ne = any
(gdb) si
0x0000000000400094 in _start ()
=> 0x0000000000400094 <_start+28>: 05 42 3b d5 mrs x5, nzcv
(gdb) si
0x0000000000400098 in _start ()
=> 0x0000000000400098 <_start+32>: 1f 20 03 d5 nop
(gdb) info registers x4 x5
x4 0x1 1
x5 0x20000000 536870912
(1)
c
cmp x0, #0
a != 0 → 结果不为零 → Z = 0
(2)
c
ccmp x1, #0, #0b0100, ne
因为a != 0,所以进行 x1, #0 的比较,
b != 0 → 结果不为零 → Z = 0
比较其实就是相当于减法,因此:
c
N = 0, Z = 0, C = 1, V = 0
因此 nzcv = 0b0010,所以 x5 = 0x20000000
(3)
c
csel x4, x2, x3, ne
b != 0 → 结果不为零 → Z = 0
所以 x4 = x2 = 1
(4)四种组合的结果
| a | b | a&&b | Z | x4 |
|---|---|---|---|---|
| 0 | 0 | 0 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 1 | 1 | 0 | 1 |
c
ccmp x1, #0, #0b0100, ne
当 a == 0 时:
b 不会参与逻辑结果
NZCV 被强制设置成 Z=1
后续逻辑只看到 "false"
因此 x4 = x3 = 0.
3.2 demo2
接下来我们来把 if (a && b || c) 用 CCMP + CSEL 做成 完全无分支 的版本。
C语言代码:
c
if (a && b || c)
y = 1;
else
y = 0;
运算符优先级是:
c
(a && b) || c
并且 规则 是:
c
若 a == 0 → a && b 为假 → 看 c
若 a != 0 && b == 0 → (a && b) 为假 → 看 c
若 a != 0 && b != 0 → (a && b) 为真 → 不看 c
若 (a && b) 为假 且 c == 0 → 假
我们分两层:
c
先算 (a && b)
如果 (a && b) 为假,再条件性地比较 c
用 最终 NZCV → CSEL
全程 无跳转
c
0x0000000000400078 in _start ()
=> 0x0000000000400078 <_start+0>: 20 00 80 d2 mov x0, #0x1 // #1
(gdb) si
0x000000000040007c in _start ()
=> 0x000000000040007c <_start+4>: 01 00 80 d2 mov x1, #0x0 // #0
(gdb) si
0x0000000000400080 in _start ()
=> 0x0000000000400080 <_start+8>: 22 00 80 d2 mov x2, #0x1 // #1
(gdb) si
0x0000000000400084 in _start ()
=> 0x0000000000400084 <_start+12>: 1f 00 00 f1 cmp x0, #0x0
(gdb) si
0x0000000000400088 in _start ()
=> 0x0000000000400088 <_start+16>: 24 18 40 fa ccmp x1, #0x0, #0x4, ne // ne = any
(gdb) si
0x000000000040008c in _start ()
=> 0x000000000040008c <_start+20>: 40 08 40 fa ccmp x2, #0x0, #0x0, eq // eq = none
(gdb) si
0x0000000000400090 in _start ()
=> 0x0000000000400090 <_start+24>: 23 00 80 d2 mov x3, #0x1 // #1
(gdb) si
0x0000000000400094 in _start ()
=> 0x0000000000400094 <_start+28>: 04 00 80 d2 mov x4, #0x0 // #0
(gdb) si
0x0000000000400098 in _start ()
=> 0x0000000000400098 <_start+32>: 65 10 84 9a csel x5, x3, x4, ne // ne = any
(gdb) si
0x000000000040009c in _start ()
=> 0x000000000040009c <_start+36>: 06 42 3b d5 mrs x6, nzcv
(gdb) si
0x00000000004000a0 in _start ()
=> 0x00000000004000a0 <_start+40>: 1f 20 03 d5 nop
(gdb) info registers x5 x6
x5 0x1 1
x6 0x20000000 536870912
(1)
c
cmp x0, #0
x0 = 1 ,不等于0,即ne
(2)
c
ccmp x1, #0, #0b0100, ne
上面的结果是不等于0,即 ne ,进行x1, #0的比较,
x1 = 0,等于0
nzcv = 0b0110
(3)
c
ccmp x2, #0, #0b0000, eq
上面的结果等于0,即eq,进行x2, #0的比较
x2 = 1 ,不等于1,即ne
nzcv = 0b0010
(4)
c
csel x5, x3, x4, ne
上面的结果是不等于0,即 ne,因此
x5 = x3 = 1
四种典型组合:
| a | b | c | 结果 | Z | x5 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 1 | 0 |
| 0 | 1 | 1 | 1 | 0 | 1 |
| 1 | 0 | 1 | 1 | 0 | 1 |
| 1 | 1 | 0 | 1 | 0 | 1 |
3.3 demo3
C 语义:
c
(a < b && c) || d
(1)先判断 a < b
若 false → (a < b && c) 为 false → 直接看 d
(2)若 a < b 为 true → 判断 c
若 c == 0 → (a < b && c) 为 false → 看 d
若 c != 0 → (a < b && c) 为 true → 不看 d
(3)最后根据 (a < b && c) || d 得结果
汇编:
我们用三步:
(1)cmp a, b
→ 得到 a < b
(2)ccmp c, #0, FALSE, lt
→ 完成 (a < b && c)
(3)ccmp d, #0, TRUE, eq
→ 完成 || d
(4)csel 消费最终 NZCV
c
.section .text
.global _start
_start:
// --------------------------------
// 输入(随便改测试)
// --------------------------------
mov x0, #3 // a
mov x1, #5 // b
mov x2, #1 // c
mov x3, #0 // d
// --------------------------------
// if ((a < b && c) || d)
// --------------------------------
// --- 1) a < b ---
cmp x0, x1 // LT if a < b
// --- 2) (a < b && c) ---
ccmp x2, #0, #0b0100, lt
// if (a < b): cmp c, 0
// else: Z = 1 (false)
// --- 3) (a < b && c) || d ---
ccmp x3, #0, #0b0000, eq
// if previous == false (Z=1): cmp d, 0
// else: Z = 0 (true)
// --------------------------------
// 结果选择
// --------------------------------
mov x4, #1 // true
mov x5, #0 // false
csel x6, x4, x5, ne
// 保存 NZCV(调试用)
mrs x7, nzcv
nop
// exit
mov x8, #93
mov x0, #0
svc #0
c
(gdb) si
0x000000000040007c in _start ()
=> 0x000000000040007c <_start+4>: a1 00 80 d2 mov x1, #0x5 // #5
(gdb) si
0x0000000000400080 in _start ()
=> 0x0000000000400080 <_start+8>: 22 00 80 d2 mov x2, #0x1 // #1
(gdb) si
0x0000000000400084 in _start ()
=> 0x0000000000400084 <_start+12>: 03 00 80 d2 mov x3, #0x0 // #0
(gdb) si
0x0000000000400088 in _start ()
=> 0x0000000000400088 <_start+16>: 1f 00 01 eb cmp x0, x1
(gdb) si
0x000000000040008c in _start ()
=> 0x000000000040008c <_start+20>: 44 b8 40 fa ccmp x2, #0x0, #0x4, lt // lt = tstop
(gdb) si
0x0000000000400090 in _start ()
=> 0x0000000000400090 <_start+24>: 60 08 40 fa ccmp x3, #0x0, #0x0, eq // eq = none
(gdb) si
0x0000000000400094 in _start ()
=> 0x0000000000400094 <_start+28>: 24 00 80 d2 mov x4, #0x1 // #1
(gdb) si
0x0000000000400098 in _start ()
=> 0x0000000000400098 <_start+32>: 05 00 80 d2 mov x5, #0x0 // #0
(gdb) si
0x000000000040009c in _start ()
=> 0x000000000040009c <_start+36>: 86 10 85 9a csel x6, x4, x5, ne // ne = any
(gdb) si
0x00000000004000a0 in _start ()
=> 0x00000000004000a0 <_start+40>: 07 42 3b d5 mrs x7, nzcv
(gdb) si
0x00000000004000a4 in _start ()
=> 0x00000000004000a4 <_start+44>: 1f 20 03 d5 nop
(gdb) info registers x6 x7
x6 0x1 1
x7 0x0 0
(1)cmp x0, x1
设置条件:
LT → a < b
GE → a >= b
这里 a < b ,因此是LT
(2)
c
ccmp x2, #0, #0b0100, lt
由于上面是 lt ,因此进行 x2, #0 的比较,x2不等于0,因此是 nq
(3)
c
ccmp x3, #0, #0b0000, eq
由于上面是nq,所以nzcv = #0b0000,即ne,不用比较x3 和 #0。
(4)
c
csel x6, x4, x5, ne
由于上面nzcv = #0b0000,即ne,因此 x6 = x4 = 1。
| a | b | c | d | 结果 |
|---|---|---|---|---|
| 3 | 5 | 1 | 0 | 1 |
| 3 | 5 | 0 | 1 | 1 |
| 5 | 3 | 1 | 1 | 1 |
| 5 | 3 | 1 | 0 | 0 |
| 3 | 5 | 1 | 1 | 1 |