Skip to content

ARM与x86指令集体系

一、概述

本节重点探讨嵌入式领域主导的ARM指令集(ARMv7 32位、ARMv8 64位)以及个人计算机主流的x86指令集(以80386为核心),并对比其与MIPS的设计。ARM和x86在市场中占据重要地位:

  • ARM:2011年,全球超90亿设备使用ARM处理器,年增长20亿,广泛应用于嵌入式和移动设备。
  • x86:自1978年Intel 8086发布,凭借兼容性和市场优势,主宰PC领域,年产量约3.5亿片。

通过分析指令集设计、寻址模式、寄存器结构、指令编码及性能优劣,揭示ARM和x86的特点,并总结常见谬误与陷阱,强调汇编编程与现代编译器的关系。

二、ARM指令集

1. ARMv7(32位)

背景

  • 1985年发布,与MIPS同期,遵循RISC(精简指令集)设计哲学,强调简洁高效。
  • 最初为Acorn RISC Machine,后改为Advanced RISC Machine。

与MIPS比较

  • 相同点
    • 发布年份:1985年。
    • 指令大小:32位。
    • 寻址空间:32位平坦。
    • 数据对齐:对齐。
    • 存储器映射:支持。
  • 不同点
    • 寄存器数量:MIPS有31个通用寄存器,ARMv7有15个。
    • 寻址模式:ARMv7提供9种复杂寻址模式,MIPS仅3种简单模式。

核心指令集

  • 算术逻辑指令
    • 加法:add(等价MIPS addu/addiu),支持溢出捕获(adds/swivs)。
    • 减法:sub(等价MIPS subu),支持溢出捕获。
    • 乘法:mul(等价MIPS mull/multu)。
    • 逻辑操作:andorr(或)、eor(异或)。
    • 移位:逻辑左移(lsl)、右移(lsr)、算术右移(asr)。
    • 比较:cmp(减法设置条件码)、cmn(加法设置条件码)、tst(逻辑与)、teq(异或)。
  • 数据传输指令
    • 加载:字节(ldrsb/ldrb)、半字(ldrsh/ldrh)、字(ldr)。
    • 存储:字节(strb)、半字(strh)、字(str)。
    • 特殊寄存器:mrs/msr(读/写)。
    • 原子交换:swp/swpb(类似MIPS ll/sc)。
  • 特点
    • 无除法指令,移位操作融入数据指令(如lsl^1move变种)。
    • 每条指令可附带移位操作,增加灵活性。

寻址模式

  • 支持9种模式,远超MIPS的3种:
    • 寄存器操作数、立即数操作数。
    • 寄存器+偏移(基址寻址)。
    • 寄存器+寄存器(下标寻址)。
    • 寄存器+寄存器倍乘(比例寻址)。
    • 寄存器+偏移/寄存器+寄存器并更新寄存器。
    • 自增/自减。
    • PC相对数据寻址。
  • 特点
    • 寄存器间接寻址与寄存器+偏移分开,支持偏移左移1或2位以扩展范围。
    • 复杂模式(如寄存器移位后加另一寄存器)支持灵活地址计算。

条件分支

  • 使用4位条件码(负值、零、进位、溢出),由算术/逻辑指令可选设置。
  • 条件执行
    • 每条指令前4位字段决定是否执行(基于条件码),可替代短分支,节省代码空间和时间。
    • 示例:条件执行可将分支优化为单指令。

独特指令

  • 立即数加载:mov Rd, Imm(12位立即数扩展为32位,循环右移)。
  • 取反:mvn Rd, -(Rsl)
  • 循环右移:ror Rd, Rsl >> i
  • 位清除:bic Rd, Rsl & -(Rs2)
  • 反向减:rsb Rd, Rs2 - Rsl
  • 多字运算:adcs/sbcs(支持进位)。
  • 块加载/存储
    • 单指令操作任意寄存器组合(16位掩码控制)。
    • 用途:保存/恢复寄存器、内存块复制。

指令格式

  • 包含4位条件执行字段,寄存器字段较小(16个寄存器)。
  • 数据传输:12位立即数;分支/跳转:24位常量。

2. ARMv8(64位)

背景

  • 2007年开始设计,2013年完成,应对32位地址空间限制。
  • 完全改进,而非简单扩展(如x86从32位到64位)。

与ARMv7的差异

  • 移除特性
    • 无条件执行字段(v7每条指令支持)。
    • 立即数字段简化为12位常量(v7为复杂函数)。
    • 移除块加载/存储指令。
    • PC不再作为寄存器,防止意外分支。
  • 新增特性
    • 32个通用寄存器(类似MIPS),包括恒零寄存器。
    • 统一字长寻址模式。
    • 添加除法指令。
    • 支持相等/不等条件分支(类似MIPS beq/bne)。

与MIPS的相似性

  • 指令集更接近MIPS,简化设计,适合编译器优化。
  • 主要区别仅在名称,ARMv8可视为MIPS的64位变种。

三、x86指令集

1. 历史演进

  • 1978:8086:16位体系结构,专用寄存器,非通用寄存器。
  • 1980:8087:添加60条浮点指令,使用栈操作。
  • 1982:80286:扩展到24位地址,增加内存保护和指令。
  • 1985:80386:32位地址/寄存器,新增寻址模式、页面支持,接近通用寄存器。
  • 1989-1995:80486、Pentium、Pentium Pro,新增4条指令(多处理、条件传送)。
  • 1997:MMX:57条多媒体指令,基于浮点栈,SIMD方式。
  • 1999:SSE:70条指令,新增128位寄存器,支持单精度浮点并行操作。
  • 2001:SSE2:144条指令,新增双精度浮点,优化编译器浮点操作。
  • 2003:AMD64:64位地址/寄存器,新增16个寄存器,长模式支持。
  • 2004:EM64T/SSE3:Intel采纳AMD64,新增13条复杂算术指令。
  • 2006:SSE4:54条指令,优化数组操作和虚拟机支持。
  • 2007:SSE5:AMD新增170条指令,包括3操作数版本。
  • 2011:AVX:扩展SSE寄存器到256位,新增128条指令。

特点

  • 35年持续扩展,平均每月新增1条指令(图2-43)。
  • 兼容性驱动发展,新增特性不破坏已有软件。
  • 复杂性高,指令集难以统一描述,开发分散。

2. 80386寄存器与寻址模式

寄存器

  • 8个32位通用寄存器(EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI)。
  • 专用寄存器:代码段、堆栈指针、数据段、指令指针(EIP)、条件码(EFLAGS)。
  • 对比:MIPS有31个通用寄存器,ARMv7有15个,x86寄存器数量少,增加分配压力。

寻址模式

  • 寄存器间接:地址在寄存器中(不可用ESP/EBP)。
  • 基址+偏移:基址寄存器+8/32位偏移(不可用ESP)。
  • 基址+比例下标:基址+2^比例*下标(比例0/1/2/3,下标不可用ESP)。
  • 基址+比例下标+偏移:组合模式。
  • 特点
    • 支持存储器操作数,指令可直接访问内存(不同于MIPS/ARMv7需显式加载)。
    • 比例因子避免下标乘4(字节地址转换),优化数组访问。
    • 等价MIPS代码需额外指令(如mullui)。

数据类型

  • 支持8位(字节)、16位(字)、32位(双字)。
  • 默认数据长度由代码段寄存器指定,可用前缀切换(如8位前缀更改默认32位)。

3. 整数操作

分类

  • 数据传送movepushpoples(加载段和寄存器)。
  • 算术逻辑addsubcmpshlshrtestincdecorxor
  • 控制流jnzjzjmpcallretloop
  • 字符串movs(复制)、lods(加载到EAX)。

特点

  • 算术/逻辑指令:一个操作数兼作源和目的(如add EAX, 6765修改EAX)。
  • 存储器操作:支持寄存器-存储器格式,增加灵活性但复杂化实现。
  • 条件分支:基于条件码,PC相对寻址以字节为单位(非固定4字节指令)。
  • 字符串指令:效率低于等效软件例程,现代程序少用。

4. 指令编码

复杂性

  • 指令长度1-15字节,取决于操作数、前缀、寻址模式。
  • 操作码包含位宽标志(w位,8/32位)、寻址模式、寄存器信息。

格式

  • 示例:JE(条件跳转,8位偏移)、CALL(32位位移)、MOV EBX, [EDI+45](后置字节+偏移)。
  • 后置字节(mod, reg, r/m):指定模式、寄存器、寄存器/存储器。
  • 比例下标模式:需额外字节(sc, index, base)。

编码规则

  • mod:0(无偏移)、1(8位偏移)、2(16/32位偏移)、3(寄存器)。
  • r/m:指定寄存器或寻址模式,32位模式下r/m=4使用比例下标。
  • 复杂编码增加编译器和硬件设计难度。

5. 总结

  • 优势
    • 兼容性确保软件生态延续,市场主导PC领域。
    • 复杂寻址模式和存储器操作减少指令数。
  • 劣势
    • 指令集复杂,硬件实现成本高。
    • 寄存器数量少,增加分配压力。
    • 在移动设备领域竞争力弱(后PC时代)。

四、谬误与陷阱

1. 谬误:更强大指令意味着更高性能

  • :x86重复前缀+move指令传输内存数据,性能低于标准寄存器操作(约慢1.5倍)或浮点寄存器操作(慢2倍)。
  • 原因:复杂指令增加时钟周期或执行时间,简单指令序列更高效。

2. 谬误:汇编语言编程获得最高性能

  • 现状:现代编译器优化(如寄存器分配)接近或超越手写汇编。
  • 风险
    • 汇编代码开发/调试时间长,可移植性差,维护困难。
    • 高级语言便于跨平台和未来机器适配。
  • :C编译器忽略手动寄存器分配提示,自动优化更优。

3. 谬误:二进制兼容性阻止指令集变化

  • 反例:x86在35年新增大量指令(每月约1条),兼容性未限制扩展。
  • 启示:指令集可通过前缀、模式等扩展功能。

4. 陷阱:字寻址误以为地址差1

  • 错误:汇编程序员常误认为连续字地址差1,而非4字节(MIPS字寻址)。
  • 解决:明确字长,地址计算加4。

5. 陷阱:指针指向已释放的局部变量

  • 错误:返回指向局部数组的指针,栈回收后指针失效。
  • 解决:遵循栈规则(图2-12),避免外部使用自动变量指针。

五、扩展与总结

1. 性能优化

  • 编译器
    • 利用流水线(第4章)和存储器层次(第5章)优化指令序列。
    • 自动寄存器分配减少手动干预。
  • 指令集设计
    • RISC(如ARM/MIPS):简单指令,易于流水化和优化。
    • CISC(如x86):复杂指令减少代码量,但硬件实现复杂。
  • ARMv8:向MIPS靠拢,简化条件执行,增加寄存器,适合现代编译器。

2. 市场与应用

  • ARM
    • 嵌入式和移动设备霸主,低功耗、高集成度。
    • ARMv8扩展64位,增强服务器和桌面潜力。
  • x86
    • PC和高性能计算核心,兼容性驱动市场。
    • 移动领域受限,需优化功耗和复杂性。

3. 未来趋势

  • ARM:64位普及,挑战x86在服务器领域的地位。
  • x86:通过AVX等扩展支持AI和高性能计算,需简化指令集以适应移动设备。
  • 编译器:持续缩小汇编与高级语言性能差距,降低汇编编程需求。

4. 总结

  • ARMv7:灵活寻址和条件执行,适合嵌入式,复杂性略高。
  • ARMv8:简化设计,接近MIPS,优化64位场景。
  • x86:兼容性强,指令复杂,适合PC但移动领域受限。
  • 关键启示
    • 指令集设计需平衡简单性与功能,RISC更易优化。
    • 现代编译器减少汇编需求,高级语言提升开发效率。
    • 地址空间扩展(如64位)是指令集长期发展的关键。