CSAPP 程序的机器级表示

machine basics

在这里插入图片描述

在内存中不存在聚合类型(数组和结构),只有连续分配的字节

X86-整数寄存器

数据格式

Intel用术语字 “word”表示116位的数据类型,32位数据被称为双字,64位数据被称为四字
标准的int被存储为4个字节、32位、双字,指针存储为8个字节4字

C声明Intel数据类型汇编代码后缀字节
char字节b1
shortw2
int双字l4
long四字q8
char*四字q8
float单精度s4



寄存器

可以存放1、2、4 或 8 个字节的“整数”数据
对于64位的系统来说,地址对应着8个字节(无类型指针)
在这里插入图片描述


在这里插入图片描述

汇编操作

数据传输指令

在内存和寄存器之间传输数据:

  • 将数据从内存加载到寄存器
  • 将寄存器的数据存入内存

操作数类型:

  1. 立即数:常量 $0x400 $-533 需要以美元符号开头,占用1、2、4个字节
  2. 寄存器:16个寄存器之一 %rsp保留作为指针
  3. 内存:寄存器给定地址处的 **8 **个连续字节的内存 (%rax) 其他寻址模式

在这里插入图片描述

注意事项:

  • 数据传输指令有4个变种:movb传输单字节、movw传送字、movl传送双字、movq传送四字
  • 数据传输指令用来传输立即数的时候,目的寄存器的大小必须和指令的字符指定的大小相同,一般而言mov只会更新目的操作数子划定的寄存器字节或者内存位置,但是movl在以寄存器作为目的时,会将高位4字节也置为0.
  • 常规的movq只能以表示为32位补码数字的立即数作为源操作数,然后将这个值进行符号扩展到64位再放进目的位置。movabsq指令能够以任意64位立即数值作为源操作数,并且只能以寄存器作为目的。
  • 数据传输指令还存在零拓展和符号扩展两种变形 P123

寻址模式

直接寻址

对应着指针引用,把寄存器和内存理解成分开的两部分,寄存器存在于CPU中
在这里插入图片描述

完整的寻址模式

在这里插入图片描述

计算地址指令​

在这里插入图片描述

程序栈放在内存中的某个区域,栈向下增长,栈顶元素的地址是所有栈中元素地址中最低的,栈指针rsp保存着栈顶元素的地址

算术和逻辑操作

详情参见P129图3.10
在这里插入图片描述

在这里插入图片描述

machine control

条件码

CMP根据两个操作数的差来设置条件码
Test 的行为与AND相同,但是不更新寄存器的值,只设置条件码。典型的操作是两个操作数相同,来判断是正数、负数还是0。 操作数为0就会置为0
访问条件码的方法
通过条件码某种组合将一个字节设为0或1 通常是一个寄存器的地位单字节寄存器或者一个单字节内存,为了得到一个32位或者64位的结果需要对高位清零(用数据传输0扩展) P137图3.14

comp:
    cmpq   %rsi, %rdi
    setl   %al
    movzbl %al, %eax
    ret

跳转指令

jmp指令是无条件跳转.他可以是无条件跳转,后面直接跟标号,也可以是间接跳转,跳转目标从寄存器或者内存位置中读出,写法" *** **"是后面跟一个操作数指示符 P139


条件语句

使用控制的条件传输

核心是写出go风格的代码,当条件满足时沿着一条执行路径执行,当条件不满足时,就走另外一条路径

	if(!test)
		goto false
 	then-statement
	goto done
fales:
	else_statement
done:	

使用数据的条件转移

计算一个条件操作的两结果,然后根据是否满足条件从中选取一个,只有在一些受限的情况下可以使用
因为现代处理器使用流水线来获得高性能,所以会进行分支预测,这种方法可以降低分支预测错误的惩罚

循环

do-while循环

loop:
	body-statement
  t=test-expr
  if(t)
  	goto loop

while循环

第一种形式

		goto test;
 loop:
 		body-statement
 test:
 		t-test-exper
    if(t)
    		goto loop	

第二种:guraded-do 转化成do-while的形式

t=test-expr
if(!t)
		goto done
loop:
		body-statement
 	 	t=test-expr
  	if(t)
  		goto loop
done:

for循环

for循环的本质是一个需要对条件状态进行更新的while循环,需要维护 init-test test-expr update-test
一般而言index是通过%rdx进行维护,看到add $1 %rdx 就可以考虑是不是for循环
因此for循环也可以转化成基于while 和基于do-while两种形式的代码

init-expr
while(test-expr){
	body-statement
  update-exper
}

但是也存在特例 P159 eg 3.29 (代码中的continue可能会阻塞对index的更新,我们需要做的就是把对于index的更新放到一个单独的程序块中,然后使用goto代替continue)

switch语句

根据一个索引值进行多重分支,通过跳转表和index的配合实现
跳转表单独存放,使用 jmp *.L4(,%rsi,8)完成跳转查询 P161
注意要点:

  1. 对于默认情况的处理
  2. 对于不存在的标号的处理是跳转到默认情况
  3. 对于结尾没有break的语句,对应的汇编代码中不存在跳转到结尾的语句



machine-procedures

过程调用(P调用Q,Q执行结束后返回P)需要包括以下几个步骤

  • 传递控制:在进入过程Q的时候,PC必须设置为Q的代码起始位置,在过程结束返回时,要把PC设置位P调用Q后的下一条指令的位置
  • 传递数据:P必须为Q提供一个或者多个参数,同时Q需要向P完成一个返回值
  • 分配和释放内存空间:在开始时,Q可能需要为局部变量分配空间,在返回前,又必须释放掉这些空间



运行时栈

x86的栈向低地址方向增长,栈指针%rsp指向栈顶元素,可以用pop和push指令将数据存入或者取出
当P调用Q时,控制和数据信息就会被添加到栈尾,当P返回时在释放掉。
当过程P 调用Q时,会把返回地址压入栈,时P相关的状态,Q的代码会把它当作当前栈的边界。通过寄存器最多可以传递6个整数值,更多的话就需要使用栈进行传递。如果有一个函数的参数可以使用寄存器传递并且是叶子过程,那么久不需要使用栈帧。

转移控制

call Q指令会把地址A(返回地址,紧跟在call指令后面的那一条指令)压入栈,同时把PC设置为Q的起始地址。对应的指令ret会从栈中pop A,并且将返回地址设置为A

数据传送

通过寄存器传递参数 P168 如果参数数量大于6,要对7~n的参数设置栈空间

栈上的局部存储

寄存器中的局部存储空间

被调用者保存寄存器
调用者保存寄存器:调用者需要自己提前在栈中保存防止被修改

递归调用

每一次函数调用都有自己的私有的状态信息存储空间 P175

long rfact(long n){
    long result;
    if(n<=1)
        result=1;
    else 
        result=n*rfact(n-1);
    return result;
}
rfact:
	pushq 	%rdx
	movq	%rdi, %rdx
	movl 	$1,	%eax
    cmp		$1,%rdi
    jle		.L35
    leaq 	-1(%rdi),%rdi
    call 	rfact
    imulq	%rbx,%rax
.L35:
	popq	%rdx
    ret

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值