3.1 指令集分类


3.1 指令集分类

本节摘要:几百条 AArch64 指令按"动谁"可以归成五大类:数据处理、访存、分支、系统控制、 SIMD 与浮点。每类有一句模板句型,类内变奏无非是宽度后缀与寻址方式。掌握分类骨架后,陌生指令只需归档,不必重学——本节给出分类判据、每类的代表指令与相互组合的典型形态。

上一章认完了牌,本章开始打牌。打牌的第一课不是背牌谱,而是先知道牌堆里有几种花色——本节就干这一件事:建立分类骨架。它值得整节的篇幅,因为后面每一章都在这个骨架上展开:访存类是第 4 章的全部,分支类是第 5 章前两节的素材,系统控制类是第 6 章的钥匙。

分类判据:一条指令动谁

给指令分类的依据只有一条:这条指令动了什么。动寄存器与立即数、产生新值的是数据处理类;在寄存器与内存之间搬运的是访存类;改变 PC 走向的是分支类;改系统状态(标志、屏蔽位、系统寄存器)的是系统控制类;动向量与浮点寄存器堆的是 SIMD 与浮点类。五大类之外的一切差别——宽度、条件、寻址——都是类内变奏。这张表把骨架先立起来:

功能类 模板句型 代表指令 典型用途
数据处理 目的, 源1, 源2 addsubmovandorr 算术逻辑运算,只碰寄存器
访存 加载/存储, 寄存器, 内存地址 ldrstrldpstnp 搬运数据,唯一能碰内存的类
分支 跳转目标 或 寄存器 bblcbztbzret 改变执行流,函数与循环的骨架
系统控制 专用操作数 msrmrssvceretdmb 特权操作、同步、屏障
SIMD 与浮点 向量目的, 向量源们 faddsmullld1uzp1 并行数据运算,第 8 章主角

判据拿来即用:看到 ldr 家族就是访存类,看到 b 开头多半是分支类,msrmrs 一出现必然涉权。反过来说,如果一条指令你认不出属于哪类,多半是它的"马甲"没认出来——比如 mov 其实是 orr(自或清零)的伪指令形态,cmpsubs(丢弃结果只留标志)的伪指令形态。

数据处理类:句型最严的一类

数据处理类的句型严格到可以当公式背:目的寄存器, 第一源, 第二源。第二源可以是寄存器、经移位的寄存器,或立即数。把常见成员按用途记成四小族最省力:算术族(addsubmulsdiv)、逻辑族(andorreormvn)、移位族(lsllsrasrror)、宽位族(sxtw 符号扩展、ubfm 位域提取)。同一句型下的变奏示例:

add x0, x1, x2 // 三寄存器:x0 = x1 + x2 add x0, x1, x2, lsl #3 // 第二源带移位:x0 = x1 + x2*8,乘 8 免乘法 adds x0, x1, x2 // s 后缀:结果写 x0 同时刷新 NZCV 标志 add x0, x1, #12 // 立即数形态:12 的编码规则见 3.2 节 mov x0, #1 // 伪指令,真实身份是 orr x0, xzr, #1 cmp x0, #5 // 伪指令,真实身份是 subs xzr, x0, #5

四条指令藏着四个必考细节:移位免费的乘 8、s 后缀与标志位的关系、立即数不是随便什么数都能编(3.2 节展开)、伪指令是给人类方便的语法糖。最后一行尤其值得咀嚼——cmp 根本不存储结果,它存在的意义就是设置标志供下一条条件指令消费,这是理解第 5 章条件码的入口。

访存类与分支类:两句高产的句型

访存类的句型是操作 + 寄存器 + 地址,变奏全在地址怎么算(第 4 章 4.1 节整节展开)。这里先记三个高频成员:ldrstr 是单寄存器搬运的主力;ldpstp 一次搬一对寄存器,是函数序言保存现场的最爱;ldrbldrh 这类宽度后缀决定搬一个字节、半字还是字。分支类句型分两支:直接跳b 无条件、b.eq 带条件)与比较加跳合一cbzcbnz 判零即跳,tbz 测位即跳)。合一支是 ARM 的特色——把"比较"与"跳转"压成一条,第 5 章写循环时会反复用到。

// 分支类两支的实际对比:同一判断的两种写法 cmp x0, #0 b.eq label // 两步:先设标志,再按标志跳 cbz x0, label // 一步:判零即跳,少一条指令

系统控制类与 SIMD 类:点到为止

系统控制类的成员不多但每条都是"特权人物":msrmrs 进出系统寄存器,svc 发起系统调用(第 2 章那趟 EL0 到 EL1 的电梯就是它按的),eret 是下楼的唯一电梯,dmbdsbisb 三个屏障指令管内存顺序(第 4 章 4.2 节的主角)。SIMD 与浮点类自成一套寄存器堆(v0v31)与一套加载指令(ld1 而非 ldr),语法上像另一种语言,第 8 章 8.2 节做向量化实验时再正面进攻——本节只需知道它们的存在与边界:普通数据处理类指令碰不到向量寄存器,反过来也一样。

实验:给一段真实反汇编贴标签

分类骨架立没立住,拿真实代码验一次最靠谱。任取一个小程序的函数,逐行贴类标签:

0000000000400790 <blend>: 400790: stp x29, x30, [sp, -32]! // 访存类:保存帧指针与返回地址 400794: mov x29, sp // 数据处理类:设帧指针 400798: ldr x2, [x1] // 访存类:取参数指向的数据 40079c: add x3, x2, x0 // 数据处理类:运算 4007a0: cbz x3, 4007b0 // 分支类:判零即跳 4007a4: mrs x0, cntvct_el0 // 系统控制类:读计时器 4007a8: scvtf d0, x0 // SIMD 浮点类:整数转浮点 4007ac: bl 400760 <emit> // 分支类:带链接跳转,调用函数 4007b0: ldp x29, x30, [sp], 32 // 访存类:恢复现场 4007b4: ret // 分支类:返回

十行代码五类齐全——这不是刻意挑的例子,而是一个普通混合函数的自然分布。统计下来你会发现典型代码里访存与数据处理类占大头,分支类密度取决于算法形状,系统控制类集中在函数边界。这个分布感在两处马上兑现:读反汇编时,类标签先行,注意力自动聚焦;写代码时,知道瓶颈多半卡在访存类,就会理解第 8 章优化为什么从内存布局讲起。

本节要点回顾

  • 判据唯一:指令动谁,就是哪一类——五类是数据处理、访存、分支、系统控制、SIMD 浮点。
  • 模板句型:每类一句型,类内变奏是宽度、条件与寻址,不产生新句型。
  • 伪指令要识破movorrcmpsubs 的马甲,识破后编码规则就统一了。
  • 合并式分支cbztbz 把比较与跳转压成一条,是写紧凑循环的常备工具。
  • 边界分明:向量寄存器堆与通用寄存器堆互不相通,两套指令各自为政。

分类骨架立住了,下一节我们拿显微镜看编码:一条 mov 的四个字节里,每个比特在干什么。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U