quicksort.c
#include
#include
#include
#include
void quickSort(int arr[], int low, int high)
{
int first = low;
int last = high;
int key = arr[first];
if(low >= high)
return;
while(first < last)
{
while(first < last && arr[last] >= key)
{
last--;
}
arr[first] = arr[last];
while(first < last && arr[first] <= key)
{
first++;
}
arr[last] = arr[first];
}
arr[first] = key;
quickSort(arr, low, first-1);
quickSort(arr, first+1, high);
}
gcc -S quicksort.c
在X86-64平台上,得到的汇编代码解释如下:
.file "b93.qsortv3.c"
.text
.globl quickSort
.type quickSort, @function
quickSort:
.LFB5:
.cfi_startproc
pushq %rbp #保存栈基到,rsp-8,
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp #新的栈基,前面已经保存了
.cfi_def_cfa_register 6
subq $32, %rsp #分配空间32B,栈顶,向下生长
movq %rdi, -24(%rbp) #第1个参数,void quickSort(int arr[], int low, int high)
movl %esi, -28(%rbp) #第2个参数
movl %edx, -32(%rbp) #第3个参数
movl -28(%rbp), %eax # int first = low;
movl %eax, -12(%rbp) #局部变量入栈
movl -32(%rbp), %eax # int last = high;
movl %eax, -8(%rbp)
movl -12(%rbp), %eax # first 又入栈?
cltq #cltq指令,特指%eax->%rax的符号拓展转换,等价于movslq %eax,%rax
leaq 0(,%rax,4), %rdx #leaq指令,类似mov指令,它左侧的数看似是给出一个地址,在内存中从给定的地址取操作数,传给右边的目的地。但其实没有取,而是直接将左侧的数对应的地址传给了右侧的目的地。这是哪里嘛?是的。first的个数*4,4表示int arr[first]的偏移
#
movq -24(%rbp), %rax # arr的首地址
addq %rdx, %rax #arr[first]的地址,其实是偏移
movl (%rax), %eax #取arr[first]的值,其实是key
movl %eax, -4(%rbp) #key入栈
movl -28(%rbp), %eax # low
cmpl -32(%rbp), %eax #if(low >= high)
# CMP指令等价于SUB,区别就是它不会把计算结果更新到目的寄存器。CMP S1,S2会计算S2-S1并根据结果设置条件码。
# low-high
jge .L12 #return
jmp .L4
.L7:
subl $1, -8(%rbp) #last--;
.L5:
movl -12(%rbp), %eax #first -> eax
cmpl -8(%rbp), %eax # first -last
jge .L6
movl -8(%rbp), %eax #move last into eax
cltq # movslq %eax,%rax
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rdx, %rax
movl (%rax), %eax # arr[last] -> eax
cmpl %eax, -4(%rbp) # key-arr[last], -4(%rbp)为key
jle .L7 #没有错
.L6:
movl -8(%rbp), %eax # last
cltq # movslq %eax,%rax
leaq 0(,%rax,4), %rdx #last*4
movq -24(%rbp), %rax # arr[]
addq %rdx, %rax #arr[last]的地址
movl -12(%rbp), %edx #first
movslq %edx, %rdx
leaq 0(,%rdx,4), %rcx # first*4
movq -24(%rbp), %rdx # arr[]
addq %rcx, %rdx #arr[first]的地址
movl (%rax), %eax #arr[last]的值
movl %eax, (%rdx) #arr[first] = arr[last];
jmp .L8
.L10:
addl $1, -12(%rbp) #first++;
.L8:
movl -12(%rbp), %eax #first
cmpl -8(%rbp), %eax # first-last
jge .L9
movl -12(%rbp), %eax #first,感觉没有必要吧,实际上可能优化了
cltq # movslq %eax,%rax
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rdx, %rax #arr[first]的地址
movl (%rax), %eax
cmpl %eax, -4(%rbp) #arr[first] <= key
jge .L10
.L9:
movl -12(%rbp), %eax
cltq
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rdx, %rax #arr[first]的地址
movl -8(%rbp), %edx
movslq %edx, %rdx
leaq 0(,%rdx,4), %rcx
movq -24(%rbp), %rdx
addq %rcx, %rdx
movl (%rax), %eax
movl %eax, (%rdx) #arr[last] = arr[first];
.L4:
movl -12(%rbp), %eax #first存入eax,int型的
cmpl -8(%rbp), %eax # first-last
jl .L5 # < 成立 while(first < last)
movl -12(%rbp), %eax #first
cltq # movslq %eax,%rax
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rax, %rdx
movl -4(%rbp), %eax #key
movl %eax, (%rdx) #arr[first] = key;
movl -12(%rbp), %eax #first
leal -1(%rax), %edx #first -1 ,第3个参数
movl -28(%rbp), %ecx # low
movq -24(%rbp), %rax #&arr
movl %ecx, %esi #第2个参数
movq %rax, %rdi #第1个参数
call quickSort #调用函数
movl -12(%rbp), %eax #first
leal 1(%rax), %ecx #first+1
movl -32(%rbp), %edx #high,第3个参数
movq -24(%rbp), %rax #&arr
movl %ecx, %esi #第2个参数
movq %rax, %rdi #第1个参数
call quickSort #调用函数
jmp .L1
.L12:
nop
.L1:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size quickSort, .-quickSort
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
原理上基本解释得清楚,数组访存时有点费脑子。
栈空间如下:
-24(%rbp): &arr
-28(%rbp): int low
-32(%rbp): int high
-12(%rbp): first
-8(%rbp): last
-4(%rbp):key