繁体   English   中英

通过引用和值传递时的gcc程序集

[英]gcc assembly when passing by reference and by value

我有一个简单的函数来计算两个双数组的乘积:

#include <stdlib.h>
#include <emmintrin.h>

struct S {
    double *x;
    double *y;
    double *z;
};

void f(S& s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

int main(void) {
    S s;
    size_t size = 4;
    posix_memalign((void **)&s.x, 16, sizeof(double) * size);
    posix_memalign((void **)&s.y, 16, sizeof(double) * size);
    posix_memalign((void **)&s.z, 16, sizeof(double) * size);
    f(s, size);
    return 0;
}

注意,函数f的第一个参数是通过引用传递的。 让我们看一下f()的结果汇编(我删除了一些不相关的部分,插入了注释并放了一些标签):

$ g++ -O3 -S asmtest.cpp 


        .globl      _Z1fR1Sm
_Z1fR1Sm:
        xorl        %eax, %eax
        testq       %rsi, %rsi
        je  .L1
.L5:
        movq        (%rdi), %r8             # array x   (1)
        movq        8(%rdi), %rcx           # array y   (2)
        movq        16(%rdi), %rdx          # array z   (3)
        movapd      (%r8,%rax,8), %xmm0     # load x[0]
        mulpd       (%rcx,%rax,8), %xmm0    # multiply x[0]*y[0]
        movaps      %xmm0, (%rdx,%rax,8)    # store to y
        addq        $2, %rax                # and loop
        cmpq        %rax, %rsi
        ja  .L5

请注意,在每次迭代时,数组x,y,z的地址都会加载到通用寄存器中,请参阅语句(1),(2),(3)。 为什么gcc不将这些指令移到循环之外?

现在制作一个结构的本地副本(不是深层副本):

void __attribute__((noinline)) f(S& args, size_t n) {
    S s = args;
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

部件:

_Z1fR1Sm:
.LFB525:
        .cfi_startproc
        xorl        %eax, %eax
        testq       %rsi, %rsi
        movq        (%rdi), %r8     # (1)
        movq        8(%rdi), %rcx   # (2)
        movq        16(%rdi), %rdx  # (3)
        je  .L1
.L5:
        movapd      (%r8,%rax,8), %xmm0
        mulpd       (%rcx,%rax,8), %xmm0
        movaps      %xmm0, (%rdx,%rax,8)
        addq        $2, %rax
        cmpq        %rax, %rsi
        ja  .L5
.L1:
        rep ret

请注意,与前面的代码不同,load(1),(2),(3)现在位于循环之外。

我将理解为什么这两个汇编代码不同。 内存别名是否与此相关? 谢谢。

$ gcc --version gcc(Debian 5.2.1-21)5.2.1 20151003

是的,gcc正在循环的每次迭代重新加载sxsy ,因为gcc不知道&s.z[i]是否为通过引用f(S&, size_t)传递的S对象的某些i别名。

使用gcc 5.2.0,将__restrict__应用于S::z ,将s引用参数应用于f() ,即:

struct S {
    double *x;
    double *y;
    double *__restrict__ z;
};

void f(S&__restrict__ s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys));
    }
    return;
}

..导致gcc生成:

__Z1fR1Sm:
LFB518:
    testq   %rsi, %rsi
    je  L1
    movq    (%rdi), %r8
    xorl    %eax, %eax
    movq    8(%rdi), %rcx
    movq    16(%rdi), %rdx
    .align 4,0x90
L4:
    movapd  (%r8,%rax,8), %xmm0
    mulpd   (%rcx,%rax,8), %xmm0
    movaps  %xmm0, (%rdx,%rax,8)
    addq    $2, %rax
    cmpq    %rax, %rsi
    ja  L4
L1:
    ret

对于Apple Clang 700.1.76,只需要在s引用上使用__restrict__

__Z1fR1Sm:                              ## @_Z1fR1Sm
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    testq   %rsi, %rsi
    je  LBB0_3
## BB#1:                                ## %.lr.ph
    movq    (%rdi), %rax
    movq    8(%rdi), %rcx
    movq    16(%rdi), %rdx
    xorl    %edi, %edi
    .align  4, 0x90
LBB0_2:                                 ## =>This Inner Loop Header: Depth=1
    movapd  (%rax,%rdi,8), %xmm0
    mulpd   (%rcx,%rdi,8), %xmm0
    movapd  %xmm0, (%rdx,%rdi,8)
    addq    $2, %rdi
    cmpq    %rsi, %rdi
    jb  LBB0_2
LBB0_3:                                 ## %._crit_edge
    popq    %rbp
    retq
    .cfi_endproc

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM