news 2026/4/23 11:09:00

ARM64 指令 --- TST/CSEL

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ARM64 指令 --- TST/CSEL

文章目录

  • 一、TST
    • 1.1 TST (immediate)
    • 1.2 TST (shifted register)
  • 二、CSEL
    • 2.1 简介
    • 2.2 CSEL的扩展指令
      • 2.2.1 CSINC
      • 2.2.2 CSINV
      • 2.2.3 CSNEG
  • 三、demo
    • 3.1 tst
      • 3.1.1 demo 1
      • 3.1.2 demo 2
    • 3.2 TST + CSEL
      • 3.2.1 demo 1
      • 3.2.2 demo 2
      • 3.2.3 总结

一、TST

1.1 TST (immediate)

TST (immediate) — Test bits (immediate)

用途: 这是一个测试指令。它在一个通用寄存器(Rn)的值和一个立即数常量之间执行逻辑 AND 操作,但不会将结果写回任何寄存器。

主要功能: 检查寄存器中的特定位,并根据结果设置条件标志。通常用于条件分支(B.cond)之前。

效果: 只会更新 PSTATE(相当于当前程序状态寄存器)中的 NZCV 条件标志位。

别名性质: 它是 ANDS(立即数)的一个谓词别名。这意味着CPU执行的操作与 ANDS(立即数) 完全相同,但当目标寄存器是零寄存器(XZR/WZR)时,汇编器使用 TST 这个助记符,使代码意图更清晰。

Encoding for the 64-bit variant:

TST<Xn>,#<imm>

等价于:

ANDS XZR,<Xn>,#<imm>

TST <Xn>, #imm 是 ANDS XZR, <Xn>, #imm 的汇编器别名。
使用 TST 更清晰地表达“仅测试”的意图,提高代码可读性。

更新 PSTATE(相当于当前程序状态寄存器)中的 NZCV 条件标志位:
N 标志:若结果最高位为 1,则 N=1
Z 标志:若结果为 0,则 Z=1(常用于判断某些位是否全为 0)
C 和 V:对于 AND/TST,通常 C=0、V=0(具体取决于架构定义,但一般不依赖它们)

典型用途:
用于测试寄存器中某些特定位是否被置位,而不修改寄存器内容。

示例:

TST X0,#0xF// 测试 X0 的低 4 位是否全为 0B.EQ label// 如果 Z=1(即低4位为0),则跳转

等价于:

ANDS XZR,X0,#0xF// XZR 是零寄存器,结果被丢弃B.EQ label

1.2 TST (shifted register)

用途: 这是一个测试指令。它在一个寄存器(Rn)的值和另一个经过可选移位操作的寄存器(Rm)值之间执行逻辑 AND 操作,但不会将结果写回任何寄存器。

主要功能: 检查寄存器中经过移位对齐后的特定位组合,并根据结果设置条件标志。常用于更灵活的位测试场景。

效果: 只更新 PSTATE 中的 NZCV 条件标志位。

别名性质: 它是 ANDS(移位寄存器)的一个谓词别名。当目标寄存器是零寄存器(XZR/WZR)时,使用 TST 助记符。

支持的移位类型
与 ANDS 相同,TST 支持以下移位操作:
LSL(逻辑左移):Rm << shift_amount
LSR(逻辑右移):Rm >> shift_amount(无符号)
ASR(算术右移):Rm >> shift_amount(有符号,符号扩展)
ROR(循环右移):Rm 循环右移

Encoding for the 64-bit variant:

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

等价于:

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

典型用途:
用于测试一个寄存器中某些由另一个寄存器(可能移位后)指定的位是否被置位。

二、CSEL

2.1 简介

Encoding for the 64-bit variant:

CSEL<Xd>,<Xn>,<Xm>,<cond>

Xd:目标寄存器
Xn:第一个源寄存器(条件为真时选择)
Xm:第二个源寄存器(条件为假时选择)
cond:条件代码(如 EQ, NE, GT, LT 等)

全称: Conditional Select

功能: 根据条件标志(NZCV)的状态,从两个源寄存器中选择一个的值写入目标寄存器。

执行方式:
如果指定的条件为真,将第一个源寄存器的值写入目标寄存器。
如果指定的条件为假,将第二个源寄存器的值写入目标寄存器。

特点: 避免了传统的条件分支,提高代码效率和性能。

不修改任何条件标志(NZCV)。
条件基于执行 CSEL 前最近一次更新标志的指令(如 CMP, TST, SUBS, ADDS 等)。

cond:

cond<cond>0000EQ0001NE0010CS0011CC0100MI0101PL0110VS0111VC1000HI1001LS1010GE1011LT1100GT1101LE1110AL1111NV

2.2 CSEL的扩展指令

ARMv8-A还提供了CSEL的扩展指令,增强了功能:
CSINC:条件选择并加 1(常用于实现 ? : 0/1)
CSINV:条件选择并对第二操作数按位取反
CNEG:条件选择并对第二操作数取负
这些指令共同构成 AArch64 的条件选择指令族,用于高效实现分支逻辑。

相关指令的关系:

ARMv8-A架构条件执行指令族中的算术成员:CSEL(基本选择)与 CSINC(递增)、CSINV(位取反),CSNEG(取负)共同构成了完整的无分支条件操作体系。通过消除算术条件操作中的分支,这些指令显著提升了数值密集型代码的性能。

2.2.1 CSINC

Encoding for the 64-bit variant:

CSINC<Xd>,<Xn>,<Xm>,<cond>

全称: Conditional Select Increment

功能: 基于条件,在两个源寄存器值之间选择,但当条件为假时,会对第二个源寄存器的值加1。

执行方式:
如果条件为真,将第一个源寄存器的值写入目标寄存器。
如果条件为假,将第二个源寄存器的值加1写入目标寄存器。

数学表达: Rd = cond ? Rn : (Rm + 1)

不修改条件标志(NZCV)。
是 CINC 和 CSET 等汇编别名的底层实现指令。

2.2.2 CSINV

Encoding for the 64-bit variant:

CSINV<Xd>,<Xn>,<Xm>,<cond>

全称: Conditional Select Invert

功能: 基于条件,在两个源寄存器值之间选择,但当条件为假时,会对第二个源寄存器的值进行按位取反。

执行方式:
如果条件为真,将第一个源寄存器的值写入目标寄存器。
如果条件为假,将第二个源寄存器的值按位取反写入目标寄存器。

数学表达: Rd = cond ? Rn : ~Rm

不修改 NZCV 条件标志。
是汇编别名 CINV 和 CSETM 的底层实现指令。

2.2.3 CSNEG

Encoding for the 64-bit variant:

CSNEG<Xd>,<Xn>,<Xm>,<cond>

全称: Conditional Select Negation

功能: 基于条件,在两个源寄存器值之间选择,但当条件为假时,会对第二个源寄存器的值进行算术取负(计算其相反数)。

执行方式:
如果条件为真,将第一个源寄存器的值写入目标寄存器。
如果条件为假,将第二个源寄存器的值的相反数写入目标寄存器。

数学表达: Rd = cond ? Rn : -Rm

不修改 NZCV 条件标志。
是汇编别名 CNEG 本身的底层实现。

三、demo

3.1 tst

3.1.1 demo 1

.section.text.global _start _start:// -------------------------------// case 1: bit 测试失败 → Z = 1 → b.eq// -------------------------------mov x0,#0b0000 tst x0,#0b1000// 0 & 8 == 0 → Z=1b.eq bit_clear bit_set:mov x1,#1// 不应该走到这里b done bit_clear:mov x1,#0// 应该走到这里done:// 把 NZCV 存下来,方便 GDB 看mrs x2,nzcv nop// exitmov x8,#93mov x0,#0svc #0
$ as-o tst_branch_demo.o tst_branch_demo.s $ ld-o tst_branch_demo tst_branch_demo.o $ gdb./tst_branch_demo......(gdb)set disassemble-next-lineon(gdb)starti Starting program:tst_branch_demo
0x0000000000400078in_start()=>0x0000000000400078<_start+0>:000080d2 mov x0,#0x0// #0(gdb)si0x000000000040007cin_start()=>0x000000000040007c<_start+4>:1f007d f2 tst x0,#0x8(gdb)si0x0000000000400080in_start()=>0x0000000000400080<_start+8>:60000054b.eq0x40008c<bit_clear>// b.none(gdb)si0x000000000040008cinbit_clear()=>0x000000000040008c<bit_clear+0>:010080d2 mov x1,#0x0// #0(gdb)info registers x1 x10x00(gdb)si0x0000000000400090indone()=>0x0000000000400090<done+0>:02423b d5 mrs x2,nzcv(gdb)si0x0000000000400094indone()=>0x0000000000400094<done+4>:1f2003d5nop(gdb)info registers x1 x2 x10x00x20x400000001073741824

(1)单步到 tst

(gdb)si0x000000000040007cin_start()=>0x000000000040007c<_start+4>:1f007d f2 tst x0,#0x8

(2)再一步,看分支

(gdb)si0x0000000000400080in_start()=>0x0000000000400080<_start+8>:60000054b.eq0x40008c<bit_clear>// b.none

(3)再 si 一次

(gdb)si0x000000000040008cinbit_clear()=>0x000000000040008c<bit_clear+0>:010080d2 mov x1,#0x0// #0

说明:
确实跳到了 bit_clear

验证寄存器:

(gdb)info registers x1 x2 x10x00x20x400000001073741824

N=0,Z = 1,C=0,V=0

3.1.2 demo 2

改一个数,分支立刻反转:

mov x0,#0b1000 tst x0,#0b1000// 8 & 8 != 0 → Z=0b.eq bit_clear

这次:

b.eq 不跳 会执行 bit_set x1=1NZCV=0
.section.text.global _start _start:// -------------------------------// case 1: bit 测试失败 → Z = 1 → b.eq// -------------------------------mov x0,#0b1000 tst x0,#0b1000// 8 & 8 != 0 → Z=0b.eq bit_clear bit_set:mov x1,#1// 不应该走到这里b done bit_clear:mov x1,#0// 应该走到这里done:// 把 NZCV 存下来,方便 GDB 看mrs x2,nzcv nop// exitmov x8,#93mov x0,#0svc #0

结果:

(gdb)starti Starting program:tst_branch_demo1 Program stopped.0x0000000000400078in_start()=>0x0000000000400078<_start+0>:000180d2 mov x0,#0x8// #8(gdb)si0x000000000040007cin_start()=>0x000000000040007c<_start+4>:1f007d f2 tst x0,#0x8(gdb)si0x0000000000400080in_start()=>0x0000000000400080<_start+8>:60000054b.eq0x40008c<bit_clear>// b.none(gdb)si0x0000000000400084inbit_set()=>0x0000000000400084<bit_set+0>:210080d2 mov x1,#0x1// #1(gdb)si0x0000000000400088inbit_set()=>0x0000000000400088<bit_set+4>:02000014b0x400090<done>(gdb)si0x0000000000400090indone()=>0x0000000000400090<done+0>:02423b d5 mrs x2,nzcv(gdb)si0x0000000000400094indone()=>0x0000000000400094<done+4>:1f2003d5nop(gdb)info registers x1 x2 x10x11x20x00

3.2 TST + CSEL

3.2.1 demo 1

用 TST + CSEL 在 ARM64 上实现 无分支 if–else。

等价的 C 代码:

if(x&0x8)y=1;elsey=0;

我们现在要做的是:
不用 b.eq / b.ne
只靠 TST + CSEL

核心指令模型:

tst x0,#0x8// 设置 Zcsel x1,x_true,x_false,ne

含义是:
如果 Z == 0(NE) → 选 x_true
否则 → 选 x_false

.section.text.global _start _start:// --------------------------------// x0 = 输入值// --------------------------------mov x0,#0b1000// ← 改成 0b0000 试试// --------------------------------// if (x0 & 0x8)// x1 = 1;// else// x1 = 0;// --------------------------------tst x0,#0x8// 只改 NZCVmov x2,#1// true 值mov x3,#0// false 值csel x1,x2,x3,ne// Z==0 ? x2 : x3// 保存 NZCV 供 GDB 查看mrs x4,nzcv nop// exitmov x8,#93mov x0,#0svc #0

CSEL 不跳转,只是根据 NZCV 拷贝一个寄存器。

(gdb)set disassemble-next-lineon(gdb)starti Starting program:tst_csel_demo Program stopped.0x0000000000400078in_start()=>0x0000000000400078<_start+0>:000180d2 mov x0,#0x8// #8(gdb)si0x000000000040007cin_start()=>0x000000000040007c<_start+4>:1f007d f2 tst x0,#0x8(gdb)si0x0000000000400080in_start()=>0x0000000000400080<_start+8>:220080d2 mov x2,#0x1// #1(gdb)si0x0000000000400084in_start()=>0x0000000000400084<_start+12>:030080d2 mov x3,#0x0// #0(gdb)si0x0000000000400088in_start()=>0x0000000000400088<_start+16>:4110839a csel x1,x2,x3,ne// ne = any(gdb)si0x000000000040008cin_start()=>0x000000000040008c<_start+20>:04423b d5 mrs x4,nzcv(gdb)si0x0000000000400090in_start()=>0x0000000000400090<_start+24>:1f2003d5nop(gdb)info registers x0 x1 x4 x00x88x10x11x40x00

(1)x0 = 0b1000

tst x0,#0x8// 8 & 8 != 0 → Z = 0

(2)NZCV:

Z=0

(3)CSEL 执行:

csel x1,x2,x3,ne// NE = Z==0 → true

(4)结果:

x1=1x4=0x00000000

3.2.2 demo 2

.section.text.global _start _start:// --------------------------------// x0 = 输入值// --------------------------------mov x0,#0b0000// --------------------------------// if (x0 & 0x8)// x1 = 1;// else// x1 = 0;// --------------------------------tst x0,#0x8// 只改 NZCVmov x2,#1// true 值mov x3,#0// false 值csel x1,x2,x3,ne// Z==0 ? x2 : x3// 保存 NZCV 供 GDB 查看mrs x4,nzcv nop// exitmov x8,#93mov x0,#0svc #0
(gdb)set disassemble-next-lineon(gdb)starti Starting program:tst_csel_demo Program stopped.0x0000000000400078in_start()=>0x0000000000400078<_start+0>:000080d2 mov x0,#0x0// #0(gdb)si0x000000000040007cin_start()=>0x000000000040007c<_start+4>:1f007d f2 tst x0,#0x8(gdb)si0x0000000000400080in_start()=>0x0000000000400080<_start+8>:220080d2 mov x2,#0x1// #1(gdb)si0x0000000000400084in_start()=>0x0000000000400084<_start+12>:030080d2 mov x3,#0x0// #0(gdb)si0x0000000000400088in_start()=>0x0000000000400088<_start+16>:4110839a csel x1,x2,x3,ne// ne = any(gdb)si0x000000000040008cin_start()=>0x000000000040008c<_start+20>:04423b d5 mrs x4,nzcv(gdb)si0x0000000000400090in_start()=>0x0000000000400090<_start+24>:1f2003d5nop(gdb)info x0 x1 x4 Undefined info command:"x0 x1 x4".Try"help info".(gdb)info registers x0 x1 x4 x00x00x10x00x40x400000001073741824

(1)x0 = 0b0000

tst x0,#0x8// 0 & 8 == 0 → Z = 1

(2)NZCV:

Z=1

(3)CSEL 执行:

csel x1,x2,x3,ne// NE 不满足 → 选 false

(4)结果:

x1=0x4=0x40000000// Z 位

3.2.3 总结

把它抽象成“公式”

tst cond,mask csel dst,val_true,val_false,ne

等价于:

dst=(cond&mask)?val_true:val_false;

TST → 只负责产生 Z
CSEL → 只消费 NZCV

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 7:40:38

AI编程助手选型指南:面向Java开发者的深度评测与实战建议

如今&#xff0c;AI编程助手已成为开发者提升效率的关键工具。然而&#xff0c;面对市场上纷繁复杂的选项——如GitHub Copilot、通义灵码、Cursor Pro、豆包MarsCode、Trae等——如何为Java开发&#xff08;尤其是SpringBoot项目&#xff09;选择最适合的工具&#xff1f;本文…

作者头像 李华
网站建设 2026/4/22 16:30:25

9 个降AI率工具,研究生必看!

9 个降AI率工具&#xff0c;研究生必看&#xff01; AI降重工具&#xff1a;论文写作的得力助手 在当今学术研究日益数字化的背景下&#xff0c;越来越多研究生开始关注论文的AIGC率问题。随着AI技术的广泛应用&#xff0c;许多学生在撰写论文时会借助AI工具进行内容生成或辅助…

作者头像 李华
网站建设 2026/4/18 2:44:29

Kotaemon Azure Machine Learning 服务对接

Kotaemon 与 Azure Machine Learning 的深度集成实践 在企业智能化转型加速的今天&#xff0c;越来越多组织开始构建基于大语言模型的智能客服、知识助手和自动化代理。然而&#xff0c;从原型验证到生产落地的过程中&#xff0c;团队常常面临环境不一致、部署复杂、运维困难等…

作者头像 李华
网站建设 2026/4/16 16:13:48

文件是否存在

目录 window系统判断&#xff1a; 需要加双引号&#xff1a; python 判断&#xff1a; window系统判断&#xff1a; 需要加双引号&#xff1a; dir "D:\Program Files\Epic Games\Launcher\Engine\Binaries\Win64\EpicGamesLauncher.exe" /a-d if exist "…

作者头像 李华