4.CPU体系架构-指令系统
本文主要介绍了汇编语言的基本概念及其与处理器的关系。文章详细讲解了汇编语言的结构特点,包括机器字长、操作数个数、操作数顺序、大小端问题以及指令类型(算术逻辑指令、控制指令和数据传送指令)。此外,文章还探讨了跳跃指令的命名规则及其在不同处理器中的实现方式,并重点分析了MIPS处理器的特点——即其没有条件标志位而依赖操作数判断执行条件的情况。文章还对比了ARM和JAL指令的区别,并通过具体例子展示了如何利用条件执行来优化程序代码的效率与密度。
在掌握了处理器寄存器以及寻址方式的知识后,终于迎来了指令系统这一重要组成部分。可以说,这是久违的亮点,令人不禁为之惊叹——因为指令系统(即汇编语言)与程序员之间拉近了一步,使得他们之间的技术交流更加直接和高效。当一名程序员遇到需要调试BUG时,必须将其反汇编为汇编语言才能彻底搞懂其运行机制及潜在问题所在。而那些令人生畏的BUG无异于久未 surfaces 的怪兽,只有真正 skilled 的开发者才能将其制服并深入剖析根源所在。然而,即使是最高 level 的技术也有其局限性——其修为深浅直接影响其境界高低,想要达到顶尖水平还需不断精进技艺方能不负众望。
我们所指的是汇编语言,请特别注意区分寻址方式中的机器指令。在此处不打算深入讲解各种处理器(X86、MIPS、ARM)的指令集系统架构(ISA)。我将尝试总结它们的共同点,并着重指出最容易让人混淆的地方。
指令集体系架构(ISA)是什么?它通常被称为Architecture(架构),这是处理器的一个抽象描述。而processor architecture在 处理器 中 的 实现 ,则被称为Microarchitecture ( 微 架 结 )。 ISA的作用就是 提取 编程人员所需了解的硬體資訊 ,從硬體系統中進行 抽象 。從程式設計者的觀點來看 , ISA則 包含 一套 指令 集 和若干寄存器。這部分內容可參考《大話Processor》書中的第三章〈指教集合體架構-Processor的外表〉。
汇编语言格式总结
以下是经过改写的文本
跳转指令的命名
跳转指令又被称作分支指令。该指令通过引导PC指针转向新的内存位置来实现对目标地址的定位,并进而影响整个程序流程的走向。借助该指令机制,在计算机系统中实现函数调用、条件判断控制以及循环操作等功能成为可能。
MIPS架构对跳转指令使用Motorola命名规则
相对位移跳转指令被称作branch;而绝对地址跳转操作则被称为jump。在汇编语言中,则分别以英文单词的首字母b和j来标识这两种指令类型。
子程序的调用被称为jump and link或branch and link操作。这些操作通常以al位结尾,并通过取and和link两个操作码的第一个字母来构造汇编指令。与普通跳转不同的是,在使用jump进行子程序调用时会将返回地址(return address)保存在寄存器ra中。
所有的PC相对跳转指令都是有条件的,例如需要比较两个寄存器。例如:
b label指令本质上是:beq $zero,$zero,off
AI助手
j指令的最大可达256M的空间,并拥有28根地址线设计。其中高位4位由当前CPU的PC寄存器提供数值支持。称其为绝对地址跳转的操作其实并不十分合适。当需要进行更大范围的跳跃操作时,则必须结合寄存器regs配合使用j指令进行操作。这种设计方式也是实现任意跳跃访问整个4G内存空间的独特手段。例如,在具体实现中我们通常采用的方式包括:使用j r或jr r指令序列进行操作。
jal指令:其返回地址由当前程序计数器PC值加8得到,并被存储在专门的返回地址寄存器RA中。这一设计背后的原因是什么呢?这一设计背后的原因是什么呢?与MIPS架构中分支时延槽相关的机制有关。
从字面上来看, 相对跳转与绝对跳转的概念并不复杂。从字面上来看, 相对跳转与绝对跳转的概念并不复杂。从字面上来看, 相对跳转与绝对跳转的概念并不复杂。
条件标志&条件执行
条件执行的明确界定是基于状态位是否被读取以判断指令是否执行。因此,在MIPS架构中依据寄存器是否为零来选择执行相应指令的操作属于基本逻辑操作范畴而非条件执行部分。
条件执行必须同时满足下面两个条件:
- 机器码中的代码字段:明确指示指令执行所需的必要前提。
- 例如,在ARM汇编语言体系下。
- AL(always execute)字段作为缺省选项字段。
- 例如,在ARM汇编语言体系下。
- 状态标志存储于CPU指定的状态寄存器单元内。
- 如8086系统中的状态寄存器fr以及ARM系统中的程序状态寄存器cpsr。
- 通常每个二进制位对应一种特定的操作类型或逻辑状态。
- 如8086系统中的状态寄存器fr以及ARM系统中的程序状态寄存器cpsr。
条件执行指的是仅在某个特定条件下(即条件标志字段满足要求时),指令才会被执行。这一特征有助于减少分支指令的数量,并提升系统性能以及程序效率。下面我们将对比ARM、MIPS和X86架构中关于条件标志与条件执行的相关内容。
对于8086处理器而言,在状态标志寄存器fr中设置了9位来表示状态信息。这其中包括6个状态标记位以及3个控制标记位。其中的6个状态标记位被专门命名为条件标志位。
对于ARM处理器来说,在程序状态寄存器cpsr中设置了4位用于条件判断。这4个位置分别是从最高有效位开始的负号(n)、零(z)、进位(c)以及溢出(v)等信息。
至于MIPS处理器由于缺乏专门的条件判断逻辑,在其架构中也没有设置条件标志位相关的功能。
MIPS没有条件标志位,不能使用条件标志来实现条件执行,但是MIPS使用其他的方法去判断是否执行某条指令,例如,使用寄存器是否为zero来作为该指令执行的条件。MIPS中的条件move指令就是这样做的。又例如,MIPS的分支指令执行都是有条件的,这里的条件就是比较寄存器的值是否相同。MIPS中的这些举措,也就是为了弥补没有条件执行的缺陷。
ARM条件标志举例
绝大多数ARM数据操作指令具备条件执行特性。详见《ARM嵌入式系统开发-软件设计与优化》P76。本节将通过经典的求解最大公约数问题(GCD, Greatest Common Divisor)来阐述条件执行指令的功能作用。
GCD算法的C语言实现:
- int gcd(int a,int b)
- {
- while(a != b)
- {
- if(a > b)
- a = a - b;
- else
- b = b - a;
- }
- return a;
- }
在不使用条件执行时,汇编指令实现GCD算法:
- gcd
- CMP r0,r1
- BEQ end
- BLT less
- SUBS r0,r0,r1
- B gcd
- less
- SUBS r1,r1,r0
- B gcd
- end
在使用条件执行是,汇编指令实现GCD算法:
- gcd
- CMP r0,r1
- SUBGT r0,r0,r1
- SUBLE r1,r1,r0
- BNE gcd
在所述算法中,在依据相应的状态标志决定是否执行的情况下使用SUBGT和SUBLE指令这一组合能够显著地减少代码长度。
这些汇编指令是程序员基于处理器支持条件执行的特点而对汇编代码进行优化的结果。为了探讨这一可能性,请看以下关于gcd C语言函数的编码与反汇编结果。这些结果分别来自x86 ARM和MIPS架构下的交叉 compilation测试。
- gcd_x86.o: 文件格式 pe-i386
- Disassembly of section .text:
- 00000000 <_gcd>:
- 0: 55 push %ebp
- 1: 89 e5 mov %esp,%ebp
- 3: 8b 45 08 mov 0x8(%ebp),%eax
- 6: 3b 45 0c cmp 0xc(%ebp),%eax
- 9: 74 1a je 25 <_gcd+0x25>
- b: 8b 45 08 mov 0x8(%ebp),%eax
- e: 3b 45 0c cmp 0xc(%ebp),%eax
- 11: 7e 08 jle 1b <_gcd+0x1b>
- 13: 8b 45 0c mov 0xc(%ebp),%eax
- 16: 29 45 08 sub %eax,0x8(%ebp)
- 19: eb e8 jmp 3 <_gcd+0x3>
- 1b: 8b 55 08 mov 0x8(%ebp),%edx
- 1e: 8d 45 0c lea 0xc(%ebp),%eax
- 21: 29 10 sub %edx,(%eax)
- 23: eb de jmp 3 <_gcd+0x3>
- 25: 8b 45 08 mov 0x8(%ebp),%eax
- 28: 5d pop %ebp
- 29: c3 ret
- 2a: 90 nop
- 2b: 90 nop
- =====================分割线=================================
- gcd_arm.o: file format elf32-littlearm
- Disassembly of section .text:
- 00000000
: - 0: e1a0c00d mov r12, sp
- 4: e92dd800 stmdb sp!, {r11, r12, lr, pc}
- 8: e24cb004 sub r11, r12, #4 ; 0x4
- c: e24dd008 sub sp, sp, #8 ; 0x8
- 10: e50b0010 str r0, [r11, -#16]
- 14: e50b1014 str r1, [r11, -#20]
- 18: e51b3010 ldr r3, [r11, -#16]
- 1c: e51b2014 ldr r2, [r11, -#20]
- 20: e1530002 cmp r3, r2
- 24: 1a000009 bne 2c <gcd+0x2c>
- 28: ea000017 b 64 <gcd+0x64>
- 2c: e51b3010 ldr r3, [r11, -#16]
- 30: e51b2014 ldr r2, [r11, -#20]
- 34: e1530002 cmp r3, r2
- 38: da000012 ble 50 <gcd+0x50>
- 3c: e51b3010 ldr r3, [r11, -#16]
- 40: e51b2014 ldr r2, [r11, -#20]
- 44: e0623003 rsb r3, r2, r3
- 48: e50b3010 str r3, [r11, -#16]
- 4c: ea000016 b 60 <gcd+0x60>
- 50: e51b3014 ldr r3, [r11, -#20]
- 54: e51b2010 ldr r2, [r11, -#16]
- 58: e0623003 rsb r3, r2, r3
- 5c: e50b3014 str r3, [r11, -#20]
- 60: ea000004 b 18 <gcd+0x18>
- 64: e51b3010 ldr r3, [r11, -#16]
- 68: e1a00003 mov r0, r3
- 6c: ea00001a b 70 <gcd+0x70>
- 70: e91ba800 ldmdb r11, {r11, sp, pc}
- =====================分割线=================================
- gcd_mips.o: file format elf32-bigmips
- Disassembly of section .text:
- 0000000000000000
: - 0: 27bdfff8 addiu sp,sp,-8
- 4: afbe0000 sw s8,0(sp)
- 8: 03a0f021 move s8,sp
- c: afc40008 sw a0,8(s8)
- 10: afc5000c sw a1,12(s8)
- 14: 8fc30008 lw v1,8(s8)
- 18: 8fc2000c lw v0,12(s8)
- 1c: 00000000 nop
- 20: 14620003 bne v1,v0,30 <gcd+0x30>
- 24: 00000000 nop
- 28: 0800001e j 78 <gcd+0x78>
- 2c: 00000000 nop
- 30: 8fc20008 lw v0,8(s8)
- 34: 8fc3000c lw v1,12(s8)
- 38: 00000000 nop
- 3c: 0062102a slt v0,v1,$v0
- 40: 10400007 beqz $v0,60 <gcd+0x60>
- 44: 00000000 nop
- 48: 8fc20008 lw v0,8(s8)
- 4c: 8fc3000c lw v1,12(s8)
- 50: 00000000 nop
- 54: 00431023 subu v0,v0,$v1
- 58: 08000005 j 14 <gcd+0x14>
- 5c: afc20008 sw v0,8(s8)
- 60: 8fc2000c lw v0,12(s8)
- 64: 8fc30008 lw v1,8(s8)
- 68: 00000000 nop
- 6c: 00431023 subu v0,v0,$v1
- 70: 08000005 j 14 <gcd+0x14>
- 74: afc2000c sw v0,12(s8)
- 78: 8fc20008 lw v0,8(s8)
- 7c: 03c0e821 move sp,s8
- 80: 8fbe0000 lw s8,0(sp)
- 84: 03e00008 jr $ra
- 88: 27bd0008 addiu sp,sp,8
- 8c: 00000000 nop
可以观察到,在编译成C语言后,在ARM架构中并未采用条件执行指令。此外,在每次完成比较操作后,该变量会被移回至内存区域。由此可见此方法在此场景下并未显示出显著优势。这种状况为何会出现呢?是否需要在编辑阶段启用相关优化选项?或者这个问题与所使用的编译器有关?目前所用的编译器是否过于老旧?这些问题目前尚无法得到解答。
MIPS条件move举例
详见《See MIPS Run》P225。
