快速排序汇编代码分析

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

 

你可能感兴趣的:(汇编,基础算法)