这个问题与gcc(4.6.3 Ubuntu)及其在为具有即时操作数的SSE内在函数的展开循环中的行为有关。
具有立即操作数的内在函数的示例是_mm_blend_ps。它期望一个4位立即数整数,它只能是一个常数。但是,使用-O3选项,编译器显然会自动展开循环(如果循环计数器值可以在编译时确定)并生成具有不同立即值的相应混合指令的多个实例。
这是一个简单的测试代码(blendsimple.c),它贯穿了blend的立即操作数的16个可能值:
#include <stdio.h>
#include <x86intrin.h>
#define PRINT(V) \
printf("%s: ", #V); \
for (i = 3; i >= 0; i--) printf("%3g ", V[i]); \
printf("\n");
int
main()
{
__m128 a = _mm_set_ps(1, 2, 3, 4);
__m128 b = _mm_set_ps(5, 6, 7, 8);
int i;
PRINT(a);
PRINT(b);
unsigned mask;
__m128 r;
for (mask = 0; mask < 16; mask++) {
r = _mm_blend_ps(a, b, mask);
PRINT(r);
}
return 0;
}
可以使用
编译此代码gcc -Wall -march=native -O3 -o blendsimple blendsimple.c
并且代码有效。显然,编译器会展开循环并为直接操作数插入常量。
但是,如果使用
编译代码gcc -Wall -march=native -O2 -o blendsimple blendsimple.c
混合内在函数会出现以下错误:
error: the last argument must be a 4-bit immediate
现在我试图找出哪个特定的编译器标志在-O3中处于活动状态但在-O2中没有,这允许编译器展开循环,但是失败了。关注gcc在线文档
https://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/Overall-Options.html
我执行了以下命令:
gcc -c -Q -O3 --help=optimizers > /tmp/O3-opts
gcc -c -Q -O2 --help=optimizers > /tmp/O2-opts
diff /tmp/O2-opts /tmp/O3-opts | grep enabled
列出-O3启用的所有选项,但不包括-O2。当我添加除了-O2
之外的所有7个列出的标志gcc -Wall -march=native -O2 -fgcse-after-reload -finline-functions -fipa-cp-clone -fpredictive-commoning -ftree-loop-distribute-patterns -ftree-vectorize -funswitch-loops blendsimple blendsimple.c
我希望行为与-O3完全相同。但是,编译器抱怨&#34;最后一个参数必须是4位立即&#34;。
有谁知道问题是什么?我认为最好知道启用这种类型的循环展开所需的标志,以便可以使用#pragma GCC优化或函数属性有选择地激活它。
(我也很惊讶-O3显然甚至没有启用unroll-loops选项。)
如果有任何帮助,我将不胜感激。这是关于我给出的SSE编程的演讲。
编辑:非常感谢您的评论。 jtaylor似乎是对的。无论优化级别如何,我都抓住了两个较新版本的gcc(4.7.3,4.8.2)和4.8.2对即时问题的抱怨。 Moverover,我后来注意到gcc 4.6.3使用-O2 -funroll-loops编译代码,但这在4.8.2中也失败了。显然,人们不能相信这个功能,应该始终展开&#34;手动&#34;正如Jason R所指出的,使用cpp或模板。
答案 0 :(得分:1)
我不确定这是否适用于您的情况,因为我不熟悉SSE内在函数。但通常,您可以告诉编译器使用以下方法专门优化代码段:
#pragma GCC push_options
#pragma GCC optimize ("unroll-loops")
do your stuff
#pragma GCC pop_options