ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

程序的机器级表示(二)

2021-12-22 23:32:20  阅读:321  来源: 互联网

标签:表示 操作数 机器 程序 rdx long 指令 寄存器 rax


注:以下所有内容均来自开源学习组织DataWhale

程序的机器级表示(二)

1 加载有效地址

**leaq S, D → \rightarrow → Load Effective Address **

注:x86-64位处理器地址长度都是64,因此都是leaq

例如如下指令:

leaq 7(%rdx, %rdx, 4), %rax:表示把有效地址复制到寄存器rax中。

其中7(%rdx, %rdx, 4)表示有效地址(计算方式见程序的机器级表示(一)中4.0.1):形式符合公式 I m m ( r b , r i , s ) → I m m + R [ r b ] + R [ r i ] × s Imm(r_b, r_i, s)\rightarrow Imm+R[r_b]+R[r_i]\times s Imm(rb​,ri​,s)→Imm+R[rb​]+R[ri​]×s;假设rdx中保存的数值为 x x x,那么根据公式则可计算得到有效地址的值为: 7 + % r d x + % r d x × 4 = 7 + 5 x 7+\%rdx+\%rdx \times 4=7+5x 7+%rdx+%rdx×4=7+5x

注意:leaq指令不是去内存地址 7 + 5 x 7+5x 7+5x处读数据,而是将 7 + 5 x 7+5x 7+5x这个值直接写入目的寄存器rax

2 leaq用于运算

例如有C代码如下:

long scale(long x, long y, long z){
	long t = x + 4 * y + 12 * z;
	return t;
}

编译后汇编代码如下:

scale:
	leaq (%rdi, %rsi, 4), %rax
	leaq (%rdx, %rdx, 2), %rdx
	leaq (%rax, %rdx, 4), %rax
	ret

根据寄存器使用惯例,x、y、z分别保存在寄存器rdi、rsi、rdx中。

根据有效地址计算方式可分析上述汇编代码:

leaq (%rdi, %rsi, 4), %rax → \rightarrow → % r d i + 4 × % r s i = x + 4 × y \%rdi+4\times\%rsi=x+4\times y %rdi+4×%rsi=x+4×y( x + 4 × y x+4\times y x+4×y存入了rax)
leaq (%rdx, %rdx, 2), %rdx → \rightarrow → % r d x + 2 × % r d x = z + 2 × z \%rdx+2\times\%rdx=z+2\times z %rdx+2×%rdx=z+2×z ( 3 z 3z 3z存入了rdx,此时 z = 3 z z=3z z=3z)
leaq (%rax, %rdx, 4), %rax → \rightarrow → % r a x + 4 × % r d x \%rax+4\times\%rdx %rax+4×%rdx(这一步x+4y和3z都被视作整体,得到的结果存到rax)

注:比例因子只能是1,2,4,8中的一个,所以要分解12

3 一元操作和二元操作

3.1 一元操作

一元操作指令只有一个操作数,因此该操作数既是源操作数又是目的操作数,操作数可以是寄存器,也可以是内存地址。

指令影响描述
INC DD ← \leftarrow ← D + 1加1
DEC DD ← \leftarrow ← D - 1减1
NEG DD ← − \leftarrow - ←−D取负
NOT DD ← ∼ \leftarrow \sim ←∼D取补

注:取补即按位取反,然后加1。

3.2 二元操作

二元操作指令包含两个操作数,第一个操作数是源操作数,这个操作数可以是立即数、寄存器或内存地址第二个操作数既是源操作数也是目的操作数,这个操作数可以是寄存器或者内存地址,但不能是立即数

指令影响描述
ADD S, DD ← \leftarrow ← D + S
SUB S, DD ← \leftarrow ← D - S
IMUL S, DD ← \leftarrow ← D * S
XOR S, DD ← \leftarrow ← D ^ S异或
OR S, DD ← \leftarrow ← D | S
AND S, DD ← \leftarrow ← D & S

3.3 一个例子

一开始内存及寄存器中保存的数据如下图所示:在这里插入图片描述

有如下指令:

addq %rcx, (%rax)
subq %rdx, 8(%rax)
incq 16(%rax)
subq %rdx, %rax
  • addq %rcx, (%rax):将内存地址0x100内的数据与寄存器rcx相加,二者之和再存储到内存地址0x100处。这条指令执行完毕后,内存地址0x100处所存的数据由0xFF变为0x100

  • subq %rdx, 8(%rax):将内存地址0x108(0x108=0x100+8)内的数据减去寄存器rdx内的数据,得0xAB - 0x3 = 0xA8,存到内存地址0x108处。这时0x108处数据由0xAB变为0xA8。

  • incq 16(%rax):将内存地址0x110(0x100+10(这个10是16进制的,对应十进制的16))的值加1,即0x13+1=0x14。此时,0x110地址处值变为0x14。

  • subq %rdx, %rax:将寄存器rax内的值减寄存器rdx内的值,即0x100 - 0x3 = 0xFD,存入寄存器寄存器rax内,此时,寄存器rax内值由0x100变为0xFD。

4 移位操作

左移指令SALSHL,二者效果相同,都是在右边填零

右移指令

  • 算术右移:SAR,左边填符号位

  • 逻辑右移:SHR,左边填零

指令影响描述
SAL k, DD ← \leftarrow ← D << k左移
SHL k, DD ← \leftarrow ← D << k左移,等同于SAL
SAR k, DD ← \leftarrow ← D >> A _A A​ k算术右移
SHR k, DD ← \leftarrow ← D >> L _L L​ k逻辑右移

移位量k:可以是一个立即数,或者是放在寄存器cl中的数。(移位指令只允许以特定的寄存器cl作为操作数

寄存器cl的长度为8,原则上移位量的编码范围可达 2 8 − 1 2^8-1 28−1 。实际上对于 w 位的操作数进行移位操作,移位量是由寄存器 cl 的低 m 位来决定。

在这里插入图片描述

对于指令salb,当目的操作数为8位,移位量由cl的低3位决定。
在这里插入图片描述
b → \rightarrow → byte,为8位,即 2 3 位 2^3位 23位,对应2进制3位

对于指令salw,移位量由cl的低4位决定

在这里插入图片描述

w → \rightarrow → word,为16位,即 2 4 2^4 24,对应2进制4位

4.1 一个例子

例如有C代码:

long arith(long x, long y, long z){
 	long t1 = x ^ y;
 	long t2 = z * 48;
	long t3 = t1 & 0xF0F0F0F;
	long t4 = t2 - t3;
 	return t4;
7 }

其中long t2 = z * 48;的汇编指令对应:

leaq (%rdx, %rdx, 2), %rax
salq $4, %rax

leaq (%rdx, %rdx, 2), %rax:即rdx中的值×3放到rax中

salq $4, %rax:rax左移四位,即rax中的值×16

通过这两条指令就实现了×48的操作。

5 汇编操作的一些特殊算术指令

指令描述
imulq S有负号全乘法
mulq S无符号全乘法
cqto转化为八字
idivq S有符号除法
divq S无符号除法

6 条件码

ALU 除了执行算术和逻辑运算指令外,还会根据该运算的结果去设置条件码寄存器。如图中所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-necvrnJP-1640185893504)(D:\DataWhale\ComputerSystem\Task 03:程序的机器级表示\条件码.png)]

6.1 条件码寄存器

条件码寄存器由CPU维护,长度是单个比特位。

在这里插入图片描述

  • CF(Carry Flag,进位标志):当CPU最近执行的一条指令最高位发生进位时,CF会被置为1。

  • ZF(Zero Flag,零标志):当操作结果等于零时,ZF会被置为1。

  • SF(Sign Flag,符号标志):当操作结果小于零时,SF会被置为1,大于零会被置为0。

  • OF(Overflow Flag,溢出标志):针对有符号数,最近的操作导致正溢出或负溢出时,OF会被置为1。

6.2 访问条件码

条件码寄存器的值ALU在执行算术和运算指令时写入,下表中这些算术和逻辑运算指令都会改变条件码寄存器的内容。

Unary OperationsBinary OperationsShift Operations
INC DADD S, DSAL k, D
DEC DSUB S, DSHL k, D
NEG DIMUL S, DSAR k, D
OR S, DSHR k, D
XOR S, D
AND S, D

例如:

  • xor指令:进位标志(CF)和溢出标志(OF)会置0

  • inc和dec:溢出标志(OF)和零标志(ZF)会被设置,但不会改变进位标志(CF)

此外还有两类指令可以设置条件码寄存器,但不会改变目的寄存器的值

  • cmp指令:根据两个操作数的差设置条件码寄存器,与减法指令sub类型。
  • test指令:与and指令类似。

6.3 示例

6.3.1 单一条件码判断条件

有C代码如下:

int comp(long a, long b){
	return (a == b);
}

对应汇编代码如下(a in %rdi, b in %rsi):

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

cmpq %rsi, %rdi:当a-b=0时,指令cmp会将零标志位(ZF)设置为1。

sete %al:这条指令意思是将条件码寄存器内ZF的值赋给寄存器al。

最后al赋值给eax并返回。

6.3.2 条件码组合判断

有C代码如下(a in %dil; b in %sil):

int comp(char a, char b){
	return (a < b);
}

对应汇编指令如下:

comp:
	cmpq	%sil, %dil
	setl	%al
	movzbl	%al, %eax
	ret

setl %al:如果a<b,则寄存器al置为1。(setl的l是less的意思)

cmp指令判断小于情况如下:

注:t=a-b:当a<b,t结果为负,符号位SF会被置为1;当出现正溢出或负溢出时,OF会被置为1。

Case1: a<b t<0 → \rightarrow → SF=1 OF=0 SF^OF=1(未发生溢出)

Case2: a>b t>0 → \rightarrow → SF=0 OF=0 SF^OF=0(未发生溢出)

Case3: a<b t>0 → \rightarrow → SF=0 OF=1 SF^OF=1(发生负溢出)

Case3: a>b t<0 → \rightarrow → SF=1 OF=1 SF^OF=1(发生正溢出)

因此,只有当SF^OF结果为1时,a<b才为真。

对于其他情况,同样可以通过条件码的组合来判断:

在这里插入图片描述

对于无符号数,选择的是进位标志(CF)和零标志(ZF)的组合来判断:

在这里插入图片描述

7 跳转指令与传送指令

有如下C代码:

long absdiff_se(long x, long y){
	long result;
	if(x < y){result = y - x;} else{result = x - y;}
	return result;
}

对应汇编代码如下:

absdiff_se:
	cmpq %rsi, %rdi
	jl.L4
	movq %rdi, %rax
	subq %rsi, %rax
	ret
.L4:
	movq %rsi, %rax
	subq %rdi, %rax
	ret

jl.L4会根据上一条cmpq指令设置的符号位标志SF和溢出标志OF的异或结果来判断是否顺序执行或者跳转到L4处执行,是一种跳转指令。当x>y时,指令顺序执行,当x<y时,跳转到L4处执行。

在现代处理器上,if-else机制比较简单通用,但执行效率可能会比较低。因此有另一种替代策略,C代码如下:

long comvdiff_se(long x, long y){
    long rval = y - x; long eval = x - y;
    long ntest = x >= y;
    if(ntest){rval = eval;} 
    return rval;
}

其对应汇编指令如下:

cmovdiff_se:
	movq	%rsi, %rdx
	subq	%rdi, %rdx
	movq	%rdi, %rax
	subq	%rsi, %rax
	cmpq	%rsi, %rdi
	cmovge	%rdx, %rax
	ret

其中cmovge是根据条件码的某种组合来进行有条件的传送数据,是一种条件传送指令。当x>y时,才会执行这一条指令。

  • 基于条件传送指令的代码会比基于跳转指令的代码效率要高

8 循环

while、do-while、for循环都是通过条件测试指令(如cmpq)和跳转指令组合来实现的。

9 switch语句

switch语句是通过跳转表来实现的。

例如:C代码如下:

void switch_eg(long x, long n, long *dest){
    long val = x;
    switch(n){
        case 0: val *= 13; break;
        case 2: val += 10; break;
        case 3: val += 11; break;
        case 4:
        case 6: val += 11; break;
        default: val = 0;
    }
    *dest = val;	
}

其中switch部分对应跳转表:

case4和case6情况相同:在这里插入图片描述

case1和case5情况相同:在这里插入图片描述

switch相比使用一组很长的if-else效率更高。

标签:表示,操作数,机器,程序,rdx,long,指令,寄存器,rax
来源: https://blog.csdn.net/tangfatter/article/details/122097188

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有