本次笔记内容:
07.寻址模式与数据传输指令等-2
承接上次笔记内容,对变址寻址进行讨论:
D(Rb, Ri, S) : Mem[Reg[Rb] + S * Reg[Ri]]
其他变形还有:
(Rb, Ri) : Mem[Reg[Rb] + Reg[Ri]] D(Rb, Ri) : Mem[Reg[Rb] + Reg[Ri] + D] (Rb, Ri, S) : Mem[Reg[Rb] + S * Reg[Ri]]
首先,已知%edx = 0xf000,%ecx = 0x100.
Expression | Computation | Address |
---|---|---|
0x8(%edx) | 0xf000 + 0x8 | 0xf008 |
(%edx, %ecx) | 0xf000 + 0x100 | 0xf100 |
(%edx, %ecx, 4) | 0xf000 + 4*0x100 | 0xf400 |
0x80 (, %edx, 2) | 2*0xf000 + 0x80) | 0x1e080) |
MOV S, D 表示将S移动到D(在AT&T汇编格式中):
MOVS S, D 表示将符号扩展的S移动到D(在AT&T汇编格式中):
MOVZ S, D 表示将零扩展的S移动到D(在AT&T汇编格式中):
pushl S 表示将双字压栈;popl D 表示将双字出栈。之后的课程会具体讨论。
leal Src, Dest
使用实例:
双操作数指令:
Format | Computation |
---|---|
addl Src, Dest | Dest = Dest + Src |
subl Src, Dest | Dest = Dest - Src |
imull Src, Dest | Dest = Dest * Src |
sall Src, Dest | Dest = Dest << Src 与shll等价 |
sarl Src, Dest | Dest = Dest >> Src 算数右移 |
shrl Src, Dest | Dest = Dest >> Src 逻辑右移 |
xorl Src, Dest | Dest = Dest ^ Src 异或 |
andl Src, Dest | Dest = Dest & Src |
orl Src, Dest | Dest = Dest |
单操作数指令:
Format | Computation |
---|---|
incl Dest | Dest = Dest + 1 |
decl Dest | Dest = Dest - 1 |
negl Dest | Dest = - Dest |
notl Dest | Dest = ~ Dest |
int arith(int x, int y, int z) { int t1 = x + y; int t2 = z + t1; int t3 = x + 4; int t4 = y * 48; int t5 = t3 + t4; int rval = t2 * t5; return rval; }
arith: pushl %ebp movl %esp, %ebp # codes above are for Set up movl 8(%ebp), %eax movl 12(%ebp), %edx leal (%edx, %eax), %ecx leal (%edx, %edx, 2), %edx sall $4, %edx addl 16(%ebp), %ecx leal 4(%edx, %eax), %eax imull %ecx, %eax # codes above are for Body movl %ebp, %esp popl %ebp ret # Finish
在进入Body之前,获得如下结构:
其中,ebp表示基址。栈中,返回地址为ebp + 4,x、y、z分别也被压入栈中(符合函数调用传参规范)。
1 movl 8(%ebp), %eax # eax = x 2 movl 12(%ebp), %edx # edx = y 3 leal (%edx, %eax), %ecx # ecx = x + y 4 leal (%edx, %edx, 2), %edx # edx = 3 * y 5 sall $4, %edx # edx = 48 * y (t4) 6 addl 16(%ebp), %ecx # ecx = z + t1 (t2) 7 leal 4(%edx, %eax), %eax # eax = 4 + t4 + x (t5) 8 imull %ecx, %eax # eax = t5 * t2 (rval)
对于第3条指令,用lea指令将x+y值放在ecx中。相当于c语言过程的局不变量。如果寄存器够用,将局部变量放在寄存器中,否者放在栈中。
对于第4、5条指令,首先乘3,已达到最终乘以48的效果:
注意到,第3条指令计算t1,第4、5条t4,第6条t2,第7条t5,与c中的顺序并不一致。
int logical(int x, int y) { int t1 = x^y; int t2 = t1 >> 17; int mask = (1<<13) - 7; int t4 = y * 48; int rval = t2 & mask; return rval; }
logical: pushl %ebp movl %esp, %ebp # codes above are for Set up movl 8(%ebp), %eax xorl 12(%ebp), %eax sarl $17, %eax andl $8185, %eax # codes above are for Body movl %ebp, %esp popl %ebp ret # Finish
只看Body,先不讨论栈的分配与回收:
1 movl 8(%ebp), %eax eax = x 2 xorl 12(%ebp), %eax eax = x ^ y (t1) 3 sarl $17, %eax eax = t1 >> 17 (t2) 4 andl $8185, %eax eax = t2 & 8185
c中,return一个32位整型,放在eax中;如果32位中return一个long long类型(64位),放在edx + eax中。如果在64位机器中,则直接放在rax中。
Size of C Objects (in Bytes)
C Data Type | Typical 32-bit | Intel IA32 | x86-64 |
---|---|---|---|
unsigned | 4 | 4 | 4 |
int | 4 | 4 | 4 |
long int | 4 | 4 | 8 |
char | 1 | 1 | 1 |
short | 2 | 2 | 2 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long double | 8 | 10/12 | 16 |
char * | 4 | 4 | 8 |
如果返回数在32位及以下,那么一个eax就够了。
64位进行了扩展:
void swap (int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; }
32位下:
pushl %ebp movl %esp, %ebp pushl %ebx // 以上为Set Up movl 12(%ebp), %ecx movl 8(%ebp), %edx movl (%ecx), %eax movl (%edx), %ebx movl %eax, (%edx) movl %ebx, (%ecx) // 以上为Body movl -4(%ebp), %ebx movl %ebp, %esp popl %ebp ret // Finsih
将两个地址取出,放在ecx与edx中,再将ecx与edx的地址的数取出,放在eax和ebx中,之后在eax和ebx中的数取出,放在edx与ecx中,即可返回。
而64位下:
movl (%rdi), %edx movl (%rsi), %eax movl %eax, (%rdi) movl %edx, (%rsi) retq
注意到64位中该功能非常简洁,可以注意到64位情况下传参是用过寄存器来传递的,而32位中寄存器不够用,参数用栈Stack来传递。
64位中,减少访问内存,使用寄存器来传参数。
但最多传6个参数,超过6个部分,依此从右往左压入栈中。
注意到上例中,被操作数仍然是32位,因此使用寄存器%eax和%edx,以及movl指令。
而x86-64下long int类型的swap如下。
void swap_l (long int *xp, long int *yp) { long int t0 = *xp; long int t1 = *yp; *xp = t1; *yp = t0; }
汇编代码如下:
swap_: movq (%rdi), %rdx movq (%rsi), %rax movq %rax, (%rdi) movq %rdx, (%rsi) retq
被操作数是64位:
(-masm = intel, Intel 语法)
lea eax, [ecx + ecx * 2] sub esp, 8 cmp dword ptr [ebp-8], 0 mov eax, dword ptr [eax * 4 + 100h]
leal (%ecx + %ecx * 2), %eax subl $8, %esp cmpl $0, -8(%ebp) movl $0x100 (, %eax, 4), %eax
一个函数的原型为:
int decode2(int x, int y, int z);
汇编代码为:
movl 16(%ebp), %edx subl 12(%ebp), %edx movl %edx, %eax sall $15, %eax sarl $15, %eax xorl 8(%ebp), %edx imull %edx, %eax
另外,有:
x at %ebp + 8, y at %ebp + 12, z at %ebp + 16
参数x、y和z存放在存储器中相对于寄存器%ebp中地址偏移量为8、12和16的地方,代码将返回值放在寄存器%eax中,写出等价于汇编代码的decode2的c代码。
答案为:
int decode2(int x, int y, int z) { int t1 = z - y; int t2 = (t1 << 15) >> 15; int t3 = x ^t1; int t4 = t2 * t1; return t4;
movl中出现地址表达式是用要去访问相应内存的,而leal中是用于算数的。