Y86-64指令集体系结构

目录

前言:

1.程序员可见状态

 2.Y86-64指令

3.指令编码

movq指令

整数操作指令

跳转指令

条件传送指令

call和ret指令 

push和pop指令 

halt和nop指令

4.Y86-64异常

5.Y86-64程序


前言:

本章内容是笔者学习csapp的一些读书笔记,其中大部分内容来自原书,并加入了一些自己的注释

指令系统是计算机软件和硬件交互的接口,由于x86指令集相对复杂,书中定义了一个简单的指令集 “Y86-64” ,能够满足一个处理器对于指令集的基本需求

1.程序员可见状态

在这里,“程序员” 既可以是用汇编代码写程序的人,也可以是产生机器级代码的编译器

程序员的可见状态是指:Y86-64中的每条指令都会读取或者修改处理器状态的某些部分,如程序寄存器、条件码、程序计数器(PC)、内存以及程序状态等等

Y86-64指令集体系结构_第1张图片

✨ 对于程序寄存器,相比于x86,%rsp也是被用作栈指针,而Y86-64指令集体系结构中少了程序寄存器%r15,这么做的目的降低编码的复杂度,这一点会在后面提及

✨ 条件码有3个,分别是ZF、SF、OF (有关条件码的内容可以参考)条件码、条件控制和条件传送_七月不远.的博客-CSDN博客https://blog.csdn.net/weixin_58165485/article/details/123466697?spm=1001.2014.3001.5502✨ 程序计数器(PC)存放当前正在执行指令的地址 (注意:是地址而并非内容)

✨ 程序状态码表明这条指令是否正常运行


 2.Y86-64指令

Y86-64指令集体系结构_第2张图片

相比于x86-64指令集,Y86-64指令集做了相应的简化:

 ✨movq指令

 x86-64中的movq指令在这里被分为四种,分别是:

Source operand Destination operand
irmovq Immediate Register
rrmovq Register Register
rmmovq Register Memory
mrmovq Memory Register

指令的第一个字母表示源操作数的类型,第二个字母表示目的操作数的类型,源操作数可以是立即数(i) ,寄存器(r),内存(m),目的操作数可以是寄存器(r)和内存(m)

  • irmovq指令表示将一个立即数存进一个寄存器中 
  • rrmovq指令表示将一个寄存器中的值存进一个寄存器
  • rmmovq指令表示将一个寄存器中的值存进一个内存地址所对应的内存
  • mrmovq指令表示将一个内存中的值存进一个寄存器

值得注意的是,我们不能:

  • 从一个内存地址直接传送到另一个内存地址,形如mmmovq是不被允许的
  • 将立即数直接传送到内存,形如immovq是不被允许的 

整数操作指令

Y86-64定义了4个整数操作指令,分别是加(addq),减(subq),与(andq),异或(xorq) 

  • 只能对寄存器数据进行操作 (这点很重要,x86-64中还允许对内存进行操作)
  • 会设置条件码

跳转指令 

跳转指令形如 jXX,Y86-64定义了7个跳转指令,分别是:

指令 跳转条件 描述
jmp 1 直接跳转
jle (SF ^ OF) | ZF 小于或等于 (有符号 <=)
jl SF ^ OF 小于 (有符号 <)
je ZF 相等 / 零
jne ~ZF 不相等 / 非零
jge ~(SF ^ OF) 大于或等于(有符号 >=)
jg ~(SF ^ OF) & ~ZF 大于(有符号 >)

根据分支指令的类型和条件代码的设置来选择分支 

条件传送指令 

有6个条件传送指令,形如comvXX:cmovle,cmovl,cmove,cmovne,cmovge,cmovg 

指令格式和rrmovq一样,但只有满足条件码组合时才发生传送 

call指令和ret指令

  • call指令的作用是将返回地址入栈,然后跳到目的地址
  • ret指令从这样的调用中返回

push指令和pop指令

分别实现入栈和出栈,依靠%rsp来实现 

halt指令 

halt指令会停止指令的执行,导致处理器停止,并将状态码设置为HLT 


3.指令编码

对于每条指令,Y86-64规定需要1~10个字节不等的编码长度,如ret指令只需要1个字节就能完成编码,而irmovq需要10个字节来完成编码

每条指令的第一个字节表明了指令的类型,它相当于我们的身份证,前六位指明了你所在的地址,而这个字节分为两个部分,每个部分占4个比特位,高4位表示指令代码 (icode),第4位表示指令功能 (ifun),下面会有详细介绍

当指令中有寄存器类型的操作数时,会在后面附加一个字节称为寄存器指示符字节,用来显示地给出将要操作地寄存器ID

Y86-64对于15个程序寄存器都给了一个ID,称为寄存器标识符(register ID

数字 寄存器名称 数字 寄存器名称
0 %rax 8 %r8
1 %rcx 9 %r9
2 %rdx A %r10
3 %rbx B %r11
4 %rsp C %r12
5 %rbp D %r13
6 %rsi E %r14
7 %rdi F 无寄存器

相比于x86-64省略掉寄存器%r15,是为了满足编码地简便性,我们可以用4个比特位来描述所有寄存器类型,从0000~1111刚好16个数,而不需要再多用一个比特位来保存17种情况

下面介绍每种指令的编码形式: 

movq指令

Y86-64指令集体系结构_第3张图片

以rrmovq为例,rrmovq指令是将一个寄存器的值传送到另一个寄存器中,其编码有两个字节长度

第一个字节是 2 0 ,2表示指令代码,0表示指令功能,当处理器拿到的指令形式为2 0 rA rB时,首先读到 2 0,处理器知道了这是两个寄存器之间传送指令,寄存器指示符字节给出了 rA,rB,指明了即将用到地两个寄存器ID,因此处理器就会根据这条编码,将 rA 的值传送到 rB 中

 

对于irmovq,它表示将一个立即数传送到寄存器中,其编码长度有10个字节 

3 0代表的是irmovq,处理器读到3 0便知道要将一个立即数传到寄存器,但是在这个过程中只有1个寄存器作为目的操作数被用到,因此在寄存器指示符字节源操作数中是F,表明不需要寄存器,而在后面的8字节中保存的就是这个立即数V,处理器将V传送到寄存器 rB 中

rmmovq和mrmovq中的8字节常数字是一个地址偏移量,表明将内存引用的地址是rB内的地址+偏移量D 

整数操作指令

之前已经说过,整数操作指令只能对两个寄存器中的数据进行操作,因此,只需要2个字节就能完成编码

  • 第一个字节 6 fn ,前4位 (6) 指出这是一个整数操作符,后4位(fn)则是指令功能位

Y86-64指令集体系结构_第4张图片

      fn = 0 1 2 3分别对应四种规定的整数运算 

  • 第二个字节是寄存器指示符字节,指明了即将操作的两个寄存器ID

因此,类似 6 1 2 3这种编码意思就是将寄存器 %rdx 中的值减去寄存器 %rbx 中的值,并把返回结果保存到 %rdx中

跳转指令

和整数操作指令类似,跳转指令也有功能之分,ifun部分分为0~7:

Y86-64指令集体系结构_第5张图片

 后面的Dest指明了要跳转的地址,如果满足条件,则下一条指令跳转到这个绝对地址执行

注意:分支指令和调用指令的目的一定是一个绝对地址,而不像IA32中那样使用PC相对寻址 

条件传送指令

和跳转指令类似,条件传送指令的fn分为0~6:

Y86-64指令集体系结构_第6张图片

当fn = 0时,代表无条件传送,也就是我们之前提到的rrmovq

其余的fn都是需要条件码满足条件时,才更新目的寄存器的值 

call和ret指令 

call和ret指令分别实现函数调用和返回,其中

  • call指令需要9个字节来编码,除了第一个字节外,剩下部分是一个绝对地址指向调用函数的地址 
  • ret指令返回的地址存储在栈上,故不需要在指令中给出

push和pop指令 

Y86-64指令集体系结构_第7张图片

push和pop指令分别实现入栈和出栈的操作,注意的是即使编码中只有rA一个目的操作数,但实际上这两条指令也同时调用了%rsp(栈指针) 

halt和nop指令

  • halt:停止指令的执行
  • nop:执行一个空操作 

练习:rmmovq %rsp,0x123456789abcd(%rdx) 进行编码 

rmmovq的第一个字节是40,%rsp的编号是4,%rdx编号是2,因此前两个字节编码是4042,而rmmovq的编码规则将地址偏移量放在后8字节的常数字中,故在小端机器上的编码为:

4042cdab896745230100 


4.Y86-64异常

对于Y86-64来说,程序员可见状态包括了状态码Stat,其可能的值如下:

名称 含义
1 AOK 正常操作
2 HLT 遇到器执行halt指令
3 ADR 遇到非法地址
4 INS 遇到非法指令


5.Y86-64程序

对于下面代码,用sum函数计算一个整数数组的和:

long sum(long* start, long count)
{
	long sum = 0;
	while (count) {
		sum += *start;
		start++;
		count--;
	}
	return sum;
}

x86-64(由GCC产生)和Y86-64汇编代码如下: 

Y86-64指令集体系结构_第8张图片

我们可以很清楚地看到一些差别:

  • Y86-64在第2~3行将两个数加载到两个寄存器中,因为不允许在整数操作中使用立即数,只能使用寄存器
  • Y86-64在第8~9行先将一个数从内存中取到寄存器中,再完成加法,因为不允许在内存的数与寄存器的数相加

你可能感兴趣的:(计算机系统,c语言,linux)