【发布时间】:2013-05-13 05:01:38
【问题描述】:
我有三个函数 a()、b() 和 c() 应该做同样的事情:
typedef float Builtin __attribute__ ((vector_size (16)));
typedef struct {
float values[4];
} Struct;
typedef union {
Builtin b;
Struct s;
} Union;
extern void printv(Builtin);
extern void printv(Union);
extern void printv(Struct);
int a() {
Builtin m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int b() {
Union m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
int c() {
Struct m = { 1.0, 2.0, 3.0, 4.0 };
printv(m);
}
当我编译这段代码时,我观察到以下行为:
- 在
a()中调用printv()时,所有4 个浮点数都被%xmm0传递。不会写入内存。 - 当在
b()中调用printv()时,%xmm0传递了两个浮点数,%xmm1传递了另外两个浮点数。为了完成这 4 个浮点数被加载 (.LC0) 到%xmm2并从那里加载到内存中。之后,从内存中的同一位置将 2 个浮点数读取到%xmm0,并将其他 2 个浮点数 (.LC1) 加载到%xmm1。 - 我对
c()的实际作用有点迷茫。
为什么a()、b() 和c() 不同?
这是 a() 的汇编输出:
vmovaps .LC0(%rip), %xmm0
call _Z6printvU8__vectorf
b() 的汇编输出:
vmovaps .LC0(%rip), %xmm2
vmovaps %xmm2, (%rsp)
vmovq .LC1(%rip), %xmm1
vmovq (%rsp), %xmm0
call _Z6printv5Union
以及 c() 的汇编输出:
andq $-32, %rsp
subq $32, %rsp
vmovaps .LC0(%rip), %xmm0
vmovaps %xmm0, (%rsp)
vmovq .LC2(%rip), %xmm0
vmovq 8(%rsp), %xmm1
call _Z6printv6Struct
数据:
.section .rodata.cst16,"aM",@progbits,16
.align 16
.LC0:
.long 1065353216
.long 1073741824
.long 1077936128
.long 1082130432
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC1:
.quad 4647714816524288000
.align 8
.LC2:
.quad 4611686019492741120
四元组4647714816524288000 似乎只不过是相邻长字中的浮点数3.0 和4.0。
【问题讨论】:
-
看起来这是调用约定的问题。
__m128可以直接通过寄存器传入。但是Packed必须在传递xmm0和xmm1的参数的情况下传递。简而言之,调用约定可能会阻止编译器进行这样的优化。 -
自从您发表评论后,我对问题进行了相当多的编辑。我用在头文件中找到的 GCC 内置类型替换了所有与 AVX 相关的内容,以使问题更容易理解。但我认为您对调用约定可能是正确的。但为什么呢?
标签: gcc assembly sse vectorization