通过引用和值传递时的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
注意,与前面的代码不同,加载(1)、(2)、(3)现在在循环之外。
我希望你能解释为什么这两个集合代码是不同的。记忆混叠与此有关吗?谢谢。
$ GCC——version
是的,gcc在每次循环迭代时都重新加载s.x
和s.y
,因为gcc不知道&s.z[i]
对于某些i
的别名是否是通过引用传递给f(S&, size_t)
的S
对象的一部分。
在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
相关文章:
- 内联程序集printf将整数解释为地址
- 正在解码MSVC 32位版本的程序集(作业).没有手术做什么
- C ++ GCC 内联程序集似乎不起作用
- 如何强制 GCC 以线性方式转换易失性内联程序集语句
- gcc linker - .obj dump 具有混合源代码程序集,但在 .elf 中链接时没有
- 将程序集 NASM 代码链接到 GCC
- 为什么添加内联程序集注释会导致 GCC 生成的代码发生如此根本的变化?
- 使用objdump或gcc-c将程序集指令转换为二进制指令
- 使用GCC程序集按移动次数向左/向右旋转
- gcc 中的英特尔式内联程序集
- GCC 内联程序集 JMP 地址;裸函数
- SPARC的GCC内联程序集:如何处理整数双字对
- 内联gcc程序集和局部变量(双)
- 通过引用和值传递时的GCC程序集
- GCC 的__builtin_expect程序集转储似乎总是下降分支
- gcc-编译x386程序集代码错误
- GCC内联程序集错误:无法识别块程序集操作数
- GCC 内联程序集错误:无法获取"this"的地址,这是一个右值表达式
- GCC 内联程序集错误:表达式后的垃圾"(%ebp)+4"
- 使用 GCC 编译内联程序集时出错,"shl"