• 2.1 引言
    • 指令集:一个给定计算机体系结构所包含的指令集合
    • 三种指令集:
      • X86:在PC领域和后PC时代的云计算领域占统治地位
      • ARM:
      • MIPS:一个优秀指令集
    • 存储程序概念:多种类型的指令和数据均以数字形式储存于存储器中的概念,存储程序型计算机即源于此。
  • 2.2 计算机硬件的操作
    • MIPS中表示两个变量的相加:
      • add a,b,c   将b与c相加,并放入变量a中
    • 硬件设计三条基本原则第一条:
      • 简单源于规整
  • 2.3 计算机硬件的操作数
    • 与高级语言程序设计不同,MIPS在算术运算指令的操作数是很严格的,它们必须来自寄存器
    • 寄存器由硬件直接构建数量有限,是计算机硬件设计的基本元素
    • 在MIPS体系中寄存器大小为32位
    • 字:计算机中的基本访问单位,通常是32位为一组,在MIPS体系结构中与寄存器大小相同
    • 高级语言的变量与寄存器的一个主要区别在于寄存器的数量有限,MIPS一类的典型的现代计算机中有32个寄存器
    • 硬件设计三条原则中的第二条:
      • 越小越快
    • 2.3.1 储存器操作数
      • MIPS的算术运算指令只对寄存器进行操作
      • 数据传送指令:在存储器和寄存器之间移动数据的命令
      • 地址:用于在储存器中指明某特定元素位置的值
      • 储存器就是一个很大的下标从0开始的一维数组,地址就相当于数组的下标
      • MIPS实际上是按字节编址的,而一个字是4字节
      • 取数:
        • 将数据从存储器复制到寄存器的数据传送指令叫取数(load)
        • 取数指令的格式是操作码后接着目标寄存器,再后面是用来访问存储器的常数和寄存器,常数和第二个寄存器中的值相加即得存储器地址。(lw 寄存器1  常数 寄存器2)
        • exp:g = h + A[8]
        • ($代表寄存器)
        • lw $t0, 8($s3)
        • add $s1,$s2,$t0
        • 数据传送指令中的常量称为偏移量,存放基址的寄存器($3)称为基址寄存器
      • 很多程序都用到8比特的字节类型,且大多数体系结构按字节编址。因此,一个字的地址必须和它所包括的4字节中某个地址相匹配,且连续字的地址相差4
      • 因为MIPS是按字节编址的,所以字的起始地址必须是4的倍数。(对其限制)
      • 有两种类型的字节寻址的计算机:
        • 一种使用最左边或“大端”字节的地址作为字地址;
        • 另一种使用最右边或“小端”字节的地址作为字地址。
        • MIPS采用的是大端编址。
      • 存数:
        • 将数据从寄存器复制到存储器
        • 格式:首先是操作码,接着是包含待存储数据的寄存器,然后是数组元素的偏移量,最后是基址寄存器(sw)
        • A[12] = h + A[8]
        • lw    $t0,32($s3) #lw得到A[8]
        • add $t0,$s2,$t0 #add相加
        • sw $t0,48($s3)   #sw存数
      • 编译器会尽量将最常用的变量保持在寄存器中,而将其他的变量放在存储器中,方法是使用取数/存数指令在寄存器和存储器之间传送变量。
      • 编译器会将不常使用的变量存回到存储器中的过程叫做寄存器溢出。
      • 寄存器与储存器相比,访问时间短、吞吐率高,功耗更小。
    • 2.3.2 常数或立即操作数
      • 使用常数时也需要取出常数
        • lw $t0 , AddrConstant($s1)
        • add $s3,$s3,$t0
      • 为了避免使用取数指令,我们提供其中一个操作数是常数的算术运算指令。
      • 这个常数操作数的快速加法指令叫做立即数
        • addi $s3,$s3,4
      • MIPS将寄存器$zero恒置为0(编号亦为0),根据使用频率来确定要定义的常数是加速大概率时间的另一个好办法
  • 2.4 有符号数和无符号数
    • 最低有效位:最右边一位
    • 最高有效位:最左边一位
    • 二进制补码:
      • 前导0表示正数,前导位表示负数
    • 处理二进制补码的简单方法
      1. 对二进制补码数取反的快速方法:~a+1(a+~a=-1 –》~a+1=-a)
      2. 用一个n位的二进制转化为多于n位表示的数,比如将十六位的数转化为三十二位(short int ——》int),直接将原来的16位简单复制到32位新数的低16位,符号为则以复制的方式填满新数的高16位
  • 2.5 计算机中指令的表示
    • 指令的布局形式叫做指令格式
    • 为遵循简单源于规整的原则,所有MIPS指令都是32位长
    • 指令格式:二进制数字段组成的指令表示形式
    • 机器语言:在计算机系统中用于交流的二进制表示形式
    • MIPS字段:
    • MIPS指令中各字段名称及含义:
      • op:指令的基本操作,通常称作操作码
      • rs:第一个源操作数寄存器
      • rt:第二个源操作数寄存器
      • rd:用于存放操作结果的目的寄存器
      • shamt:位移量。(2.6之前都不使用这个字段,故为0)
      • funct:功能。一般称为功能码,用于指明op字段中操作的特定变式
    • 操作码:指令中用来表示操作和格式的字段
    • 设计原则3
      • 优秀的设计需要适宜的折中方案
  • 2.6 逻辑操作
    • 左移 sll
    • 右移srl
    • 按位与 and,andi
    • 按位或 or,ori
    • 按位取反 nor
    • 第一类操作逻辑称为移位
      • sll $t2,$s0,4
    • 第二类有用的操作是按位与(AND)
      • or $t0,$t1,$t2
    • 第三类是按位取反
      • nor $t0,$t1,$t3 (NOT(A) = NOT(A OR 0)) 为了格式统一
  • 2.7 决策指令
    • 条件分支:该指令先比较两个值,然后根据比较的结果决定的是否从程序中的一个新地址开始执行指令序列
      1. (branch if equal)beq register1,register2,L1
        • 如果r1和r2相等,那么跳到标签为L1的语句
      2. (branch if not equal)bne register1,register2,L1
        • 如果r1和r2不等,那么跳到标签为r1的语句
    • exp: if ( i == j ) f = g + h; else f = g – h;
      • bne $s3,$s4,Else       #bne代码跳过then部分,效率会更高
      • add $s0,$s1,$s2
      • j Exit #jump 无条件分支指令
      • Else:sub $s0,$s1,$s2
      • Exit:
      • 这里的Exit,Else都只是自定义的标签名,使用无条件分支只是为了逻辑合理
    • 2.7.1 循环
      • exp: while ( save[i] ==k ) i += 1;
        • Loop: sll $t1,$t3,2 #由于系统按照字节寻址,所以先要将i*4才能找到地址,而左移2位相当于*4,加一个Loop标签以便在循环末端能够跳回该指令
        • add $t1,$t1,$s6     #得到save[i]的地址
        • lw $t0,0($t1)          #因为中间的位只能是常量,而一开始的偏移量用了寄存器表示,所以只能先得到地址再移动0个字节
        • bne $t0,$s5,Exit
        • addi $s3,$s3,1
        • j Loop
        • Exit:
      • slt $t0, $s3, $s4    #$t0 = 1 if $s3 < $s4
      • slti $t0,$s2,10
  • 2.8 计算机对硬件的支持
    • 过程:根据提供的参数执行一定人物的存储的子任务
    • 在过程运行中,程序必须遵循以下6个步骤
      1. 将参数放在过程可以访问的位置
      2. 将控制转交给过程
      3. 获得过程所需的存储资源
      4. 执行需要的人物
      5. 将结果的值存放在调用程序可以访问的位置
      6. 将控制返回初始点,因为一个过程可能由一个程序中的多个点调用
    • 寄存器是计算机中保存数据最快的位置,所以我们希尽可能多地使用寄存器,MIPS软件在为过程调用分配32个寄存器时遵循以下约定:
      • $a0~$a3 用于传递参数的四个参数寄存器
      • $v0~$v1 用于返回值的两个值寄存器
      • $ra          用于返回起始点的返回地址寄存器
    • 跳转和链接指令:跳转到某个地址的同时将吓一跳指令的地址保存在寄存器$ra中
      • jal ProcedureAddress
      • 指令中的链接部分表示指向调用点的地址或链接,以允许过程返回到合适的地址
      • 存储在寄存器$ra(31号寄存器)中的链接部分称为返回地址
    • 返回地址:指向调用点的链接,使过程可以返回带合适的地址,在MIPS中它存储在寄存器$ra中
    • jr $ra #用于case语句,表示无条件跳转到寄存器所指定的地址
    • 调用者:调用一个过程并为过程提供必要的参数值的程序
    • 被调用者:根据调用者提供的参数执行一系列存储的指令,然后将控制权返回调用者的过程
    • 程序计数器(PC):包含在程序中正在被执行指令地址的寄存器。(jal指令实际上将PC+4保存在寄存器$ra中,从而将链接指向下一条执行,为过程做好准备)
    • 2.8.1 使用更多的寄存器
      • 换出寄存器最理想的数据结构是栈,一种后进先出的队列。
      • 栈指针以字为单位进行调整,MIPS软件为其准备了第29号寄存器,并将其命名为$sp。
      • 放入数据:压栈(栈指针值减小)
      • 传出数据:出栈(栈指针值增大)
      • $t0~t9:10个临时寄存器,在过程中不必被调用者保存
      • $s0~s7:8个保留寄存器,在过程调用中必须被保存(一但调用,由被使用者保存和恢复)
    • 2.8.2 嵌套过程
      • 不调用其他过程的过程被称为叶过程
      • 全局指针 $gp
    • 2.8.3 在栈中为新数据分配空间
      • 栈需要存储过程的局部变量,但这些变量不适用于寄存器
      • 栈中包含过程所保存的寄存器和局部变量的片段被称为过程帧,或活动记录
      • 帧指针:指向给定过程中保存的寄存器和局部变量的值
    • 2.8.4 在堆中为新数据分配空间
      • 代码段:UNIX中目标文件中的段,包含源文件中例程对应的机器语言代码
  • 2.9 人机交互
    • ASCII码
    • lb:
      • 从内存中读出一个字节,并将其放在一个寄存器最右边的8位
    • sb:
      • 把一个寄存器最右边的8位取出来然后写到内存中
    • 复制一个字节
      • lb $t0,0($sp)
      • sb $t0,0($gp)
    • 字符串拷贝
      • C:
      • void strcpy(char x[], char y[]){
        	int i;
        	i=0;
        	while((x[i] = y[i]) != '\0')
        	i+=1;
        }
      • MIPS
      • 假定数组x和y的基地址在$a0和$a1中,而i在$s0中,将$s0保存在栈中 
      • strcpy:
      • addi $sp,$sp,-4 #减小栈指针地址4以压栈 
      • sw $s0, 0(sp)       #将s0压栈
      • add $s0 , $zero, $zero         #i=0+0
      • L1:add $t1, $s0, $a1 #这是循环的开始,y[i]地址的形成是把i加到y[]上,我们不必将i*4,因为char的长度就是一字节
      • lbu $t2, 0($t1) #用无符号字节读取指令,将y[i]中的字符放入$t2中
      • add $t3,$s0,$a0
      • sb $t2,0($t3) #复制x[i]=y[i]
      • beq $t2,$zero,L2 #如果字符是0则退出循环
      • addi $s0,$0,1 #i++
      • j L1 #跳到L1语句,循环继续
      • #如果不继续循环,那么就是到了字符串的最后一个字符,我们还原$s0和栈指针,然后返回
      • L2: lw $0,0*($sp) #还原咯 
      • addi $sp,$sp,4 #通过指针的增大4将1出栈 
      • jr $ra #return
  • 2.10 MIPS中32位立即数和寻址
    • 2.10.1 32位立即数
      • lui指令:将16位立即数常量存放到寄存器的高16位,低16位用0填充
        • lui $s0,61
        • ori $s0,$s0,2304
    • 2.10.2 分支和跳转指令
      • J型寻址
      • PC相对寻址:程序计步器=寄存器+分支地址,它将PC和指令中的常数相加作为寻址结果
      • PC相对寻址时所家的地址被设计为字地址
    • 2.10.3 MIPS寻址模式总结
      • 多种不同的寻址方式一般统称为寻址模式
        1. 立即数寻址,操作数是位于指令自身中的常数
        2. 寄存器寻址,操作数是寄存器
        3. 基址寻址或偏移寻址,操作数在内存中,其地址是指令中基址寄存器和常数的和
        4. PC相对寻址,地址是PC和指令中常数的和
        5. 伪直接寻址,跳转地址由指令中26位字段和PC高位相连而成
    • 2.10.4 机器语言解码
  • 2.11 并行与指令:同步
    • 数据竞争:假如来自不同线程的两个访存请求访问同一个地址,它们连续出现,并且至少其中一个是写操作,那么这两个存储访问行成数据竞争
  • 2.12 翻译并执行程序
    • C语言程序-》编译器-》汇编语言程序-》汇编器-》目标模块:机器语言模块+目标库:例程序(机器语言)-》链接器-》可执行代码:机器语言程序-》加载器-》存储器
    • 2.12.1 编译器
      • 编译器将C程序转换成一种机器能理解的符号形式的汇编语言程序
      • 汇编语言:一种符号语言,能被翻译成二进制的机器语言
    • 2.12.2 汇编器
      • 伪指令:汇编语言指令的一个变种,通常被看作一条汇编指令
      • 符号表:一个用来匹配标记名和指令所在内存字的地址的列表
      • MIPS汇编器将blt(小于则分支)转换成两条指令:slt和bne(其他包括bgt,bge,ble),它也将一个到远距离的分支指令拆成一个分支指令和一个跳转指令。
      • 如前所述,MIPS编译器允许将32位常量加载到一个寄存器中,不用考虑立即数指令的16位限制。
      • 总的来说,伪指令使得MIPS拥有比硬件所实现的更为丰富的汇编语言指令集。唯一的代价是保留了一个由汇编器使用的寄存器$at。
      • 汇编器同样接受不同基数的数字。除了二进制和十进制,它们通常还使用比二进制更为紧凑而又容易转换位位模式的基数。MIPS汇编器使用十六进制。
      • 汇编器的主要任务是汇编成机器代码。汇编器将汇编语言程序转换成目标文件。
      • 汇编器将汇编语言程序转换成目标文件,它包括机器语言指令、数据和指令正确放入内存所需要的信息。
      • UNIX系统中的目标文件通常包含以下6个不同的部分:
        • 目标文件头:描述目标文件其他部分的大小和配置
        • 代码段:包含机器语言代码
        • 静态数据段:包含在程序生命周期内分配的数据
        • 重定位信息,标记了一些在程序加载进内存时依赖于绝对地址的指令和数据
        • 符号表,包含未定位的剩余标记,如外部引用
        • 调试信息:包含一份说明目标模块如何编译的简明描述,这样,调试器能够将机器指令关联到C源文件,并使数据结构也变得可读
    •   2.12.3 链接器
      • 链接器:也称链接编辑器。它是一个系统程序,把各个独立汇编的机器语言程序组合起来并且解决所有未定义的标记,最后生成可执行文件
      • 链接器的工作分为3个步骤:
        1. 将代码和数据模块象征性地放入内存
        2. 决定数据和指令标签的地址
        3. 修补内部和外部引用
      • 采用链接器的原因是:修补代码比重新编译和汇编要快得多
      • 链接器产生一个可执行文件:
        • 一个具有目标文件格式的功能程序,不包含未解决的引用。它可以包含符号表和调试信息。
        • “剥离的可执行程序”不包含这些信息,可能包含加载器所需的重定位信息
    • 2.12.4 加载器
      • 假定可执行文件已经在磁盘中,在UNIX系统中,加载器按照如下步骤工作:
        1. 读取可执行文件头来确定代码段和数据段的大小
        2. 为正文和数据创建一个足够大的地址空间
        3. 将可执行文件中的指令和数据复制到内存中
        4. 把主程序的参数复制到栈顶
        5. 初始化机器寄存器,将栈指针指向第一个空位置
        6. 跳转到启动例程,它将参数复制到参数寄存器并且调用程序的main函数。当main函数返回时,启动例程通过系统调用exit终止程序
      • 加载器:把目标程序装载到内存中以准备运行的系统程序
    • 2.12.5 动态链接库
      • 在程序执行过程中才被链接的库例程
      • 它需要额外的空间来存储动态链接的信息,但是不需要i复制或链接整个库。
      • 仅仅在例程的第一次调用时开销较大 ,此后就只需要一个间接跳转。
    • 2.12.6 启动一个Java程序
      • Java的发明目的之一就是能够安全地运行在每台计算机上
      • Java程序会首先被编译成易于解释的指令序列——Java字节码指令集,而不是计算机可识别的汇编语言
      • Java虚拟机的软件解释器能够执行Java字节码文件,解释器是一个用来模拟指令集体系结构的程序
  •  2.13 以一个C排序程序作为完整的例子
    • 暂略
  • 2.14 数组与指针
    • 暂略
  •  2.15 编译C语言和解释Java语言
    • 面向对象语言:一种仅仅针对对象而不是动作的编程语言,或者针对数据而不是逻辑的编程语言
  • 2.16 实例:ARMv7(32位)指令集
    • 暂略
  • 2.17 实例:x86指令集
    • 暂略
  • 2.18 实例:ARMv8(64位)指令集
    • 暂略
  • 2.19 谬误与陷阱
    • 谬误:更强大的指令意味着更高的性能
    • 谬误:使用汇编语言编程来获得更高的性能
    • 谬误:商用计算机二进制兼容的重要性意味着成功的指令集不需要改变
    • 陷阱:忘记在字节寻址的机器中,连续的字地址不是相差1
    • 陷阱:在自动变量的定义过程外,使用指针指向该变量
  •  2.20本章小结
    • 3条准则:
      1. 简单源于规整
      2. 越小越快
      3. 优秀的设计需要好的折中
    • 每一类MIPS指令与编程语言中出现的结构相关
      • 算术指令对应于复制语句中的运算
      • 传输指令很可能发生在处理像数组和结构体这样的数据结构时
      • 条件分支被用于if语言和循环
      • 无条件分支被用于过程调用和返回以及case/switch语句
    •  

发表评论

邮箱地址不会被公开。 必填项已用*标注