我正在尝试使用霓虹灯在iPhone 4上实现点积功能。基于本教程:http://www.delmarnorth.com/microwave/requirements/neon-test-tutorial.pdf 我在XCode 4.5中尝试了以下内容。当我将nruns = 1设置为有效时,即我从霓虹灯中得到与标准C ++版本相同的答案。但是,当我设置nruns> 1个东西被破坏并且垃圾出来(例如,如果我在第一次调用dotProduct()之后打印出数组的元素,它们就会被破坏)。我必须承认我没有使用霓虹灯的经验,但我需要的是能够做到这一点产品!有人有什么想法?
float dotProduct ( float *a, float *b, int n) {
float sum=0.0f;
__asm__ volatile (
"vmov.f32 q8, #0.0 \n\t"
"vmov.f32 q9, #0.0 \n\t"
"1: \n\t"
"subs %3, %3, #8 \n\t"
"vld1.f32 {d0,d1,d2,d3}, [%1]! \n\t"
"vld1.f32 {d4,d5,d6,d7}, [%2]! \n\t"
"vmla.f32 q8, q0, q2 \n\t"
"vmla.f32 q9, q1, q3 \n\t"
"bgt 1b \n\t"
"vadd.f32 q8, q8, q9 \n\t"
"vpadd.f32 d0, d16, d17 \n\t"
"vadd.f32 %0, s0, s1 \n\t"
: "=w"(sum)
: "+r"(a), "+r"(b), "+r"(n)
: "q0", "q1", "q2", "q3", "q8", "q9");
return sum;
}
void test_dotProduct_neon()
{
int n=16, i, k;
int nruns = 2;
float dp;
float *a = new float[n];
float *b = new float[n];
for (i=0; i < n; i++) {
a[i] = (float) i;
b[i] = (float) (2*i);
}
for (i=0; i<nruns; i++) {
dp=0.0f;
for( k=0; k < n; k++) {
dp += a[k] * b[k];
}
}
printf(" C Result: %f\n", dp );
for (i=0; i<nruns; i++) {
dp = dotProduct( a, b, n);
}
printf(" Neon Result: %f\n", dp );
}
答案 0 :(得分:3)
您的NEON代码会修改指针“a”和“b”,因为您的加载指令(vld1)会递增地址寄存器(这就是地址后的'!')。
据推测,正在发生的事情是编译器没有意识到这些值可能会改变,因此你的代码第二次使用伪指针值。
你用“+”标记那些内联汇编输入,这意味着“输入/输出操作数”,但我认为你需要在输出操作数部分列出它们,而不是在输入操作数,以便工作。您的代码应该是
: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9"
此外,输入,输出和破坏寄存器的集合不不相交!将寄存器标记为clobbered并不会阻止编译器将其用作输入寄存器AFAIR,因为它假定在 clobbering发生之前输入已被消除。现在,由于您的输入是ARM寄存器,而您的寄存器寄存器是NEON寄存器,因此您应该是安全的如果,输出与修复寄存器的情况也是如此。我建议您检查http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html以确定。
编辑:即使sum
碰巧存在于其中一个破坏的寄存器中,您的代码似乎也是正确的,因为您从未读过它,只能在最后编写它。我之前在编写上面的警告时错过了。
编辑:您的内联程序集将指针a
和b
作为内存区域(带有m
- 约束)传递,但是作为普通值通用寄存器。因此,编译器不会意识到您实际上是从这些内存位置读取,因此可能会错误地将程序集块移动到其他存储区域之外。现在,由于你的内存区域有可变的大小,你不能轻易使用m
- 约束,因为那些假定大小是静态确定的。相反,您可以将"memory"
添加到clobber列表中,该列表告诉编译器程序集块对任意内存位置进行读写操作。当你在它时,你也应该添加“cc
”,因为你的汇编块修改了条件码寄存器(包含测试指令结果的寄存器)。输入/输出声明然后看起来如下
: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9", "cc", "memory"
在编写内联汇编块时,请始终记住编译器没有,绝对没有查看此类块的内容。它是有关块的行为和数据依赖性的唯一信息源是声明的输入和输出操作数,以及声明的clobber列表。并且编译器将在优化时积极地使用该信息,尤其是在使用-O2
或更高版本进行编译时。因此,这些声明中的任何遗漏都可能导致编译器生成错误的代码。