*4.45
在3.4.2节中,x86-64 pushq 指令被描述成要减少栈指针,然后将寄存器存储在栈指针的位置。因此,如果我们有一条指令形如对于某个寄存器 REG, pushq REG, 它等价千下面的代
码序列:subq $8,%rsp Decrement stack pointer movq REG, (%rsp) Store REG on stackA. 借助于练习题 4.7中所做的分析,这段代码序列正确地描述了指令 pushq %rsp 的行为吗?请解释。
B. 你该如何改写这段代码序列,使得它能够像对 REG 是其他寄存器时一样,正确地描述 REG 是%rsp 的情况?
正常来说入栈是栈指针下移,把值放到栈上,但是pushq %rsp时,如果真的有这行代码,存的值是下移前还是后就产生了歧义。4.7告诉我们存的是没有减小的栈指针,所以题目所给的汇编代码是不正确的
修改的话:
movq REG, (%rsp-8)
subq $8,%rsp
*4.46
3.4.2节中, x86-64 popq 指令被描述为将来自栈顶的结果复制到目的寄存器,然后将栈指针减少。因此,如果我们有一条指令形如 popq REG, 它等价于下面的代码序列:
>movq (%rsp), REG Read REG from stack >addq $8,%rsp Increment stack pointerA. 借助于练习题 4.8 中所做的分析,这段代码序列正确地描述了指令 popq %rsp 的行为吗?请解释。
B. 你该如何改写这段代码序列,使得它能够像对 REG 是其他寄存器时一样,正确地描述 REG rsp 的情况?
和上一题同理
movq REG, (%rsp+8)
addq $8,%rsp
***4.47
你的作业是写一个执行冒泡排序的 Y86-64 程序。下面这个函数用数组引用实现冒泡排序,供你参考:
>1 /• Bubble sort: Array version•/ >2 void bubble_a(long* data, long count) { >3 long i, last; >4 for (last = count-1; last > 0; last--) { >5 for (i = O; i < last; i++) >6 if (data [i +1] < data [i]) { >7 /* Swap adjacent elements*/ >8 long t = data[i+1]; >9 data[i+1] = data[i]; >10 data[i] = t; >11 } >12 } >13 }A. 书写并测试一个版本,它用指针引用数组元素,而不是用数组索引。
B. 书写并测试一个由这个函数和测试代码组成的 Y86-64 程序。你会发现模仿编译你的代码产生的 x86-64 代码来做实现会很有帮助。虽然指针比较通常是用无符号算术运算来实现的,但是在这个练习中,你可以使用有符号算术运算。
用指针做一个替换
void bubble_a(long* data, long count){
long i, last;
long *p = data;
for(last = count-1; last > 0; last--){
for(i = 0; i < last; i++){
long *a = data + i;
long *b = data + i + 1;
if(*a > *b){
long t = *a;
*a = *b;
*b = t;
}
}
}
}
得到的x86代码如下:
bubble_a:
.LFB23:
.cfi_startproc
endbr64
leaq -1(%rsi), %r8 ;last = count-1
testq %r8, %r8
jle .L1
leaq (%rdi,%r8,8), %rsi ;rsi = rdi + 8 * r8
jmp .L3
.L4:
addq $8, %rax ; 指向下一个元素
cmpq %rsi, %rax ; 确认是否遍历完毕
je .L7 ; 遍历完,进入L7,否则进入L5
.L5:
movq (%rax), %rdx
movq 8(%rax), %rcx
cmpq %rcx, %rdx ; 比较当前元素和下一个元素
jle .L4 ; 跳到L4,下一次循环
movq %rcx, (%rax)
movq %rdx, 8(%rax) ; 交换
jmp .L4
.L7:
subq $8, %rsi
subq $1, %r8
je .L1
.L3:
movq %rdi, %rax ;把数组的起始地址放入 %rax
jmp .L5
.L1:
ret
.cfi_endproc
转译到Y86代码
回顾Y86指令,只有8字节数据。
有指明目标的4个movq:irmovq rrmovq mrmovq rmmovq,立即数(i)、寄存器 ® 或内存 (m).
四个只能操作寄存器的整数操作addq subq andq xorq
七个跳转指令jmp jle jl je jne jge jg
六个条件传送cmovle cmovl cmove cmovne cmovge cmovg
call ret
pushq popq
halt停止处理器
接下来对照x86汇编代码完成翻译:
bubble_a:
irmovq $1, %rax ;rax = 1,立即数
rrmovq %rsi, %r8 ;r8 = count
subq %rbx, %r8 ; count - 1
jle .L1 ;last小于0,进入.L1(返回)
irmovq $8, %rbx ;rbx = 8
rrmovq %r8, %rcx ;rcx = last
andq %rcx,%rcx
jle .L1 ;如果只有一个元素,也返回
irmovq $0, %rsi ;rsi = 0
.L_mult:
addq %rbx, %rsi ;rsi += 8
subq %rbx, %rcx ;rcx -= 1
jg .L_mult ;rcx 小于等于0时
addq %rdi, %rsi ;rsi = data + last*8,完成寻址
.L3:
rrmovq %rdi, %rax ;rax = data
jmp .L5
.L5:
mrmovq (%rax), %rdx
mrmovq 8(%rax), %rcx ;拿出两个待比较数字
rrmovq %rdx, %rbx ;中继,不影响寄存器里的值
subq %rcx, %rbx ;比较
jle .L4
rmmovq %rcx, (%rax)
rmmovq %rdx, 8(%rax) ;交换
.L4:
irmovq $8, %rbx
addq %rbx, %rax ;指向下一个元素
rrmovq %rsi, %rbx
subq %rax, %rbx
je .L7 ;内层循环结束,进入外层循环
jmp .L5 ;继续内层循环
.L7:
irmovq $8, %rbx ;rbx = 8
subq %rbx, %rsi ;rsi -= 8
irmovq $1, %rbx ;rbx = 1
subq %rbx, %r8 ;r8(last)-=1
je .L1 ;last == 0,返回
jmp .L3 ;否则回到L3
.L1:
ret ;返回