了解简单的汇编程序

时间:2014-11-02 02:55:01

标签: c assembly att

我很久以前就练习了Assembler,我想了解一个简单的程序(我从C代码生成汇编代码),它添加了2个向量(实际上是2个数组)并将结果存储在另一个向量(输出数组)中。我的目标是研究矢量化。为此,我在i7核心处理器上使用Debian Wheezy下的gcc-4.9。

这里是C代码片段(不是矢量化版本):

#include <stdio.h>

#define SIZE 10000

void test(double *a, double *b, double *c)
{
  int i;

  for (i = 0; i < SIZE; i++)
  {
    c[i] = a[i] + b[i];
  }
}

int main()
{
 int i;
 double tab1[SIZE];
 double tab2[SIZE];
 double tab3[SIZE];

 for (i = 0; i < SIZE; i++)
    {
     tab1[i] = i;
     tab2[i] = i;
     tab3[i] = 0;
    }

 test(tab1, tab2, tab3);

 for (i = 0; i < SIZE; i++)
    printf(" tab3[%d] = %f\n", i, tab3[i]);

 return 0;
}

我使用AT&amp; T语法生成汇编代码:

gcc -std=c99 -c main_no_vectorized.c -O3 -S -o main_no_vectorized.s

这是汇编代码:

    .file   "main_no_vectorized.c"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB0:
    .text
.LHOTB0:
    .p2align 4,,15
    .globl  test
    .type   test, @function
test:
.LFB3:
    .cfi_startproc
    leaq    16(%rdx), %rax
    leaq    16(%rsi), %rcx
    cmpq    %rax, %rsi
    setae   %r8b
    cmpq    %rcx, %rdx
    setae   %cl
    orb %cl, %r8b
    je  .L7
    cmpq    %rax, %rdi
    leaq    16(%rdi), %rax
    setae   %cl
    cmpq    %rax, %rdx
    setae   %al
    orb %al, %cl
    je  .L7
    testb   $8, %dil
    pushq   %r12
    .cfi_def_cfa_offset 16
    .cfi_offset 12, -16
    pushq   %rbp
    .cfi_def_cfa_offset 24
    .cfi_offset 6, -24
    pushq   %rbx
    .cfi_def_cfa_offset 32
    .cfi_offset 3, -32
    je  .L8
    movsd   (%rdi), %xmm0
    movl    $9998, %ebp
    movl    $4999, %r9d
    movl    $9999, %r12d
    movl    $1, %r8d
    movl    $1, %ebx
    addsd   (%rsi), %xmm0
    movsd   %xmm0, (%rdx)
.L3:
    salq    $3, %r8
    xorl    %eax, %eax
    xorl    %ecx, %ecx
    leaq    (%rdi,%r8), %r11
    leaq    (%rsi,%r8), %r10
    addq    %rdx, %r8
    .p2align 4,,10
    .p2align 3
.L4:
    movupd  (%r10,%rax), %xmm0
    addl    $1, %ecx
    addpd   (%r11,%rax), %xmm0
    movups  %xmm0, (%r8,%rax)
    addq    $16, %rax
    cmpl    %r9d, %ecx
    jb  .L4
    cmpl    %ebp, %r12d
    leal    (%rbx,%rbp), %eax
    je  .L1
    cltq
    movsd   (%rdi,%rax,8), %xmm0
    addsd   (%rsi,%rax,8), %xmm0
    movsd   %xmm0, (%rdx,%rax,8)
.L1:
    popq    %rbx
    .cfi_remember_state
    .cfi_restore 3
    .cfi_def_cfa_offset 24
    popq    %rbp
    .cfi_restore 6
    .cfi_def_cfa_offset 16
    popq    %r12
    .cfi_restore 12
    .cfi_def_cfa_offset 8
    ret
    .p2align 4,,10
    .p2align 3
.L8:
    .cfi_restore_state
    movl    $10000, %ebp
    movl    $5000, %r9d
    movl    $10000, %r12d
    xorl    %r8d, %r8d
    xorl    %ebx, %ebx
    jmp .L3
.L7:
    .cfi_def_cfa_offset 8
    .cfi_restore 3
    .cfi_restore 6
    .cfi_restore 12
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L2:
    movsd   (%rdi,%rax), %xmm0
    addsd   (%rsi,%rax), %xmm0
    movsd   %xmm0, (%rdx,%rax)
    addq    $8, %rax
    cmpq    $80000, %rax
    jne .L2
    rep ret
    .cfi_endproc
.LFE3:
    .size   test, .-test
    .section    .text.unlikely
.LCOLDE0:
    .text
.LHOTE0:
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC3:
    .string " tab3[%d] = %f\n"
    .section    .text.unlikely
.LCOLDB4:
    .section    .text.startup,"ax",@progbits
.LHOTB4:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB4:
    .cfi_startproc
    pushq   %rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    xorl    %eax, %eax
    subq    $240016, %rsp
    .cfi_def_cfa_offset 240032
    movdqa  .LC2(%rip), %xmm3
    leaq    32(%rsp), %rcx
    leaq    80032(%rsp), %rdx
    movdqa  .LC1(%rip), %xmm1
    .p2align 4,,10
    .p2align 3
.L21:
    pshufd  $238, %xmm1, %xmm0
    cvtdq2pd    %xmm1, %xmm2
    paddd   %xmm3, %xmm1
    movaps  %xmm2, 16(%rsp,%rax)
    cvtdq2pd    %xmm0, %xmm0
    movaps  %xmm2, 80016(%rsp,%rax)
    movaps  %xmm0, (%rcx,%rax)
    movaps  %xmm0, (%rdx,%rax)
    addq    $32, %rax
    cmpq    $80000, %rax
    jne .L21
    leaq    160016(%rsp), %rdi
    movl    $80000, %edx
    xorl    %esi, %esi
    call    memset
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L22:
    movapd  16(%rsp,%rax), %xmm0
    addpd   80016(%rsp,%rax), %xmm0
    movaps  %xmm0, 160016(%rsp,%rax)
    addq    $16, %rax
    cmpq    $80000, %rax
    jne .L22
    xorl    %ebx, %ebx
    .p2align 4,,10
    .p2align 3
.L23:
    movsd   160016(%rsp,%rbx,8), %xmm4
    movl    %ebx, %esi
    movl    $.LC3, %edi
    movl    $1, %eax
    addq    $1, %rbx
    movapd  %xmm4, %xmm0
    movsd   %xmm4, 8(%rsp)
    call    printf
    cmpq    $10000, %rbx
    jne .L23
    addq    $240016, %rsp
    .cfi_def_cfa_offset 16
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE4:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE4:
    .section    .text.startup
.LHOTE4:
    .section    .rodata.cst16,"aM",@progbits,16
    .align 16
.LC1:
    .long   0
    .long   1
    .long   2
    .long   3
    .align 16
.LC2:
    .long   4
    .long   4
    .long   4
    .long   4
    .ident  "GCC: (Debian 4.9.1-16) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

您能否向我解释上述汇编代码与C代码相关的主要步骤,特别是&#34; test&#34;函数,主函数初始化循环和参数传递(即堆栈的推送和弹出指令)以及&#34; a&#34;和&#34; b&#34;数组?

什么对应.L2,.L3,...段?与L2缓存,L3缓存有关系吗?

很抱歉这些基本问题,但我从Intel x86_64汇编程序开始。

感谢您的宝贵帮助

2 个答案:

答案 0 :(得分:1)

生成的汇编代码非常复杂。它首先检查数组a,b和c是否以导致优化循环失败的方式重叠。例如,如果您这样做:

test(tab1, tab2, &tab1[1]);

然后检测到重叠并使代码跳转到L7(直接实现)。顺便说一下,L代表Label,标签号只是编译器生成的,没有特别的意义。所以L1,L2,L3等只是用于代码分支到不同位置的标签。重叠检查从.LFB3开始,到最后je .L7结束。

如果未检测到重叠,则将使用优化的循环。这个优化的循环将尝试一次添加两个双精度而不是一个。优化循环的第一件事是找出数组a是否与16字节边界(testb $8, %dil指令)对齐。如果是,它将跳转到L8以加载一组常量(例如r9 = 5000)。如果数组未对齐,if将通过并加载一组不同的常量(例如r9 = 4999),并处理第一个元素。这是因为未对齐的情况需要一次进行两次4999次迭代,并在循环外单独处理第一个和最后一个未对齐的元素。对齐的案例将只进行5000次迭代。

无论哪种方式,代码接下来达到L3。 L3和L4处的代码是优化的循环,它使用addpd指令一次添加两个(L7中的非优化循环使用addsd一次添加一个)。在L4循环结束后,它会检查是否需要处理最后一个元素(对于未对齐的情况)。然后它返回ret指令。

顺便说一下,知道调用test时,a位于rdib位于rsi,而{{} 1}}在c中。这是64位的调用约定。因此,堆栈上没有任何参数。如果您不太了解x86程序集,请专注于从L7开始的代码。这是非优化版本,你应该能够计算出那个部分,因为我说你的三个参数都在rdi,rsi和rdx中。

答案 1 :(得分:0)

.L2等标签,它们用于指代下一条指令。如果您使用goto,它们就像C中的标签一样。标签的主要用途是使用跳转或分支来指定跳转的位置。

例如,.L2标签是for (i = 0; i < SIZE; i++)test()循环正文的开头,它按8个字节计算(double的大小)最高8 * 10000。循环中的最后一条指令是jne .L2,如果先前的比较不相等,则跳转到.L2

您可能会在x64上找到this reference (PDF)帮助。

相关问题