GCC 生成不需要的汇编代码
Posted
技术标签:
【中文标题】GCC 生成不需要的汇编代码【英文标题】:GCC generates undesired assembly code 【发布时间】:2015-10-08 19:26:28 【问题描述】:我正在研究矢量化循环,而 GCC 让我很难过。 当我查看它生成的汇编代码时,我看到了很多我想去掉的奇怪的行。
例如,通过矢量化,我了解到您可以通过向 GCC 提供有关数组对齐的附加信息来避免大量额外的装配线。 http://locklessinc.com/articles/vectorize/
这是我的实验。
#define SIZE 1024
void itwillwork (const uint16_t * a, const uint16_t * b, uint16_t * comp)
int i = 0;
comp[i]=a[i]|b[i];
生成简单的程序集:
.globl _ZN8Test_LUT7performEv
23 _ZN8Test_LUT7performEv:
24 .LFB664:
25 .cfi_startproc
26 0020 488B4710 movq 16(%rdi), %rax
27 0024 488B4F08 movq 8(%rdi), %rcx
28 0028 488B5720 movq 32(%rdi), %rdx
29 002c 0FB700 movzwl (%rax), %eax
30 002f 660B01 orw (%rcx), %ax
31 0032 668902 movw %ax, (%rdx)
32 0035 C3 ret
33 .cfi_endproc
但是,即使我期待一些额外的行,我对添加循环后得到的结果感到非常惊讶:
#define SIZE 1024
void itwillwork (const uint16_t * a, const uint16_t * b, uint16_t * comp)
int i = 0;
for(i=0;i<SIZE;i++)
comp[i]=a[i]|b[i];
用更多的行生成这个程序集:
233 _Z10itwillworkPKtS0_Pt:
234 .LFB663:
235 .cfi_startproc
236 0250 488D4210 leaq 16(%rdx), %rax
237 0254 488D4E10 leaq 16(%rsi), %rcx
238 0258 4839F0 cmpq %rsi, %rax
239 025b 410F96C0 setbe %r8b
240 025f 4839CA cmpq %rcx, %rdx
241 0262 0F93C1 setnb %cl
242 0265 4108C8 orb %cl, %r8b
243 0268 743E je .L55
244 026a 4839F8 cmpq %rdi, %rax
245 026d 488D4710 leaq 16(%rdi), %rax
246 0271 0F96C1 setbe %cl
247 0274 4839C2 cmpq %rax, %rdx
248 0277 0F93C0 setnb %al
249 027a 08C1 orb %al, %cl
250 027c 742A je .L55
251 027e 31C0 xorl %eax, %eax
252 .p2align 4,,10
253 .p2align 3
254 .L57:
255 0280 F30F6F0C movdqu (%rsi,%rax), %xmm1
255 06
256 0285 F30F6F04 movdqu (%rdi,%rax), %xmm0
256 07
257 028a 660FEBC1 por %xmm1, %xmm0
258 028e F30F7F04 movdqu %xmm0, (%rdx,%rax)
258 02
259 0293 4883C010 addq $16, %rax
260 0297 483D0008 cmpq $2048, %rax
260 0000
261 029d 75E1 jne .L57
262 029f F3C3 rep ret
263 .p2align 4,,10
264 02a1 0F1F8000 .p2align 3
264 000000
265 .L55:
266 02a8 31C0 xorl %eax, %eax
267 02aa 660F1F44 .p2align 4,,10
267 0000
268 .p2align 3
269 .L58:
270 02b0 0FB70C06 movzwl (%rsi,%rax), %ecx
271 02b4 660B0C07 orw (%rdi,%rax), %cx
272 02b8 66890C02 movw %cx, (%rdx,%rax)
273 02bc 4883C002 addq $2, %rax
274 02c0 483D0008 cmpq $2048, %rax
274 0000
275 02c6 75E8 jne .L58
276 02c8 F3C3 rep ret
277 .cfi_endproc
两者都是使用 gcc 4.8.4 在发布模式下编译的,-O2 -ftree-vectorize -msse2。
有人可以帮我摆脱这些线条吗?或者,如果不可能,你能告诉我他们为什么在那里吗?
更新:
我已经尝试过 http://locklessinc.com/articles/vectorize/ 那里的技巧,但我遇到了另一个问题:
#define SIZE 1024
void itwillwork (const uint16_t * a, const uint16_t * b, uint16_t * comp)
int i = 0;
for(i=0;i<SIZE;i++)
comp[i]=a[i]|b[i];
为此功能生成了几条装配线,我明白了。 但是当我从其他地方调用这个函数时:
itwillwork(a,b,c);
没有调用指令:直接使用“itwillwork”的一长串指令(同上)。 我错过了什么吗? (“额外的行”是问题,而不是内联调用)
【问题讨论】:
一个好主意是将汇编代码粘贴到您的问题中,而不必去 pastebin 站点。我不明白您为什么在问题中放入高级代码,而不是程序集。 为什么你认为有这么多行是一件坏事?如果优化后的代码运行速度很快,你是否关心代码大小?而且,你对restrict
做了什么尝试(发布那个版本的 C++ 代码)。在我看来,asm 代码就像编译器不知道限制指针(尤其是comp
),如果是的话,效率会更高。
我试图在这里粘贴汇编代码,但它根本不可读(由于某种原因,没有换行)。
编译器没有调用您的代码,因为它已经确定代码足够小(且简单)可以内联。
编译器为这个函数产生大量代码的原因主要来自两个问题:1.指针别名,2.对齐。您可以向编译器保证您不会使用 restrict
为您的指针起别名,并且 gcc 有一个内置函数可以保证指针与 __buitin_assume_aligned()
对齐。
【参考方案1】:
你得到 "weird" 代码是因为 GCC 不能对你的指针的对齐做出假设,所以你可以看到它首先执行对齐测试以确定它是否可以走快速路径和一次执行 128 位,或者慢速路径一次执行 16 位。
此外,您发现代码重复的原因是编译器正在应用内联优化。您可以使用 __attribute((noinline))
规范禁用此功能,但如果您的目标是性能,请让编译器内联它。
如果您指定__restrict
关键字,那么编译器将只生成快速路径代码:https://goo.gl/g3jUfQ
但是,这并不意味着编译器会神奇地为您处理对齐问题,因此请注意传递给函数的内容。
【讨论】:
我试过了,效果很好,谢谢!然而,我尝试使用一个类(变量现在是属性,函数现在是方法,请参阅goo.gl/x7aP6W - 代码不应该做任何有趣的事情)。如您所见,这是“正确”编译的。但是,一旦我将此代码拆分为“.h”和“.cpp”文件,我就会得到前面提到的额外行。知道为什么吗? 原型和定义是什么样子的。以上是关于GCC 生成不需要的汇编代码的主要内容,如果未能解决你的问题,请参考以下文章