2

我正在尝试使用霓虹灯在 iPhone 4 上实现点积功能。基于本教程: http: //www.delmarnorth.com/microwave/requirements/neon-test-tutorial.pdf 我在 XCode 4.5 中尝试了以下内容。当我设置 nruns=1 时,它可以工作,即,我从霓虹灯中得到与标准 C++ 版本相同的答案。但是,当我设置 nruns > 1 时,某些东西会损坏并出现垃圾(例如,如果我在第一次调用 dotProduct() 后打印出 a 数组的元素,它们就会损坏)。我不得不承认我没有使用霓虹灯的经验,但我所需要的就是能够做这个点积!有人有什么想法吗?

   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 );
    }
4

1 回答 1

3

您的 NEON 代码会修改指针“a”和“b”,因为您的加载指令 (vld1) 会增加地址寄存器(这就是地址后的“!”)。

据推测,正在发生的事情是编译器没有意识到这些值可能会改变,因此您的代码第二次使用虚假指针值。

您确实用“+”标记了这些内联汇编输入,这意味着“输入/输出操作数”,但我认为您需要在输出操作数部分中列出它们,而不是在第二输入操作数中列出它们才能工作。您的代码应阅读

: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9"

此外,输入、输出和破坏寄存器的集合不是不相交的!将寄存器标记为已破坏并不会阻止编译器将其用作输入寄存器 AFAIR,因为它假定在发生破坏之前已消耗输入。现在,由于您的输入是 ARM 寄存器,而您的破坏寄存器是 NEON 寄存器,因此如果输出与破坏寄存器的情况并非如此,那么您应该是安全的。我建议您检查http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html以确定。

编辑:您的代码似乎是正确的,即使sum碰巧存在于其中一个被破坏的寄存器中,因为您从未读过它,它只在最后写入它。我之前在写上面的警告时错过了这一点。

编辑:您的内联程序集传递指针ab不是作为内存区域(使用m-Constraint),而是作为通用寄存器中的普通值。因此,编译器不知道您实际上是在从这些内存位置读取数据,因此可能会错误地将您的程序集块从其他存储区移到这些位置。现在,由于您的内存区域具有可变大小,您不能轻易使用m-Constraints,因为它们假定大小是静态确定的。相反,您可以添加"memory"到 clobber 列表中,它告诉编译器程序集块从任意内存位置读取和写入。当您使用它时,您还应该添加“cc",因为您的汇编块修改了条件代码寄存器(包含测试指令结果的寄存器)。然后输入/输出声明如下所示

: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9", "cc", "memory"

在编写内联汇编块时,请始终记住编译器不会,绝对不会查看此类块的内容。关于块的行为和数据依赖关系的唯一信息来源是声明的输入和输出操作数,以及声明的 clobber 列表。并且编译器在优化时积极地使用这些信息,特别是如果您使用-O2或更高版本进行编译。因此,这些声明中的任何遗漏都可能导致编译器生成不正确的代码。

于 2012-09-27T13:01:32.623 回答