NEON内联汇编 - 存储查询

时间:2015-07-29 02:39:31

标签: gcc assembly arm inline-assembly neon

我正在尝试学习如何使用gcc和内联汇编来使用NEON。 虽然它令人困惑而且进展缓慢,但我正在进行一些进展(自从我上次尝试编写程序集以来已经过了10年)。 我的简单程序加载一个(小)向量,饱和度对它进行加法并存储它。我遇到的问题是我似乎无法将结果存储在我想要的位置。 当我在输出列表中使用未使用的数组指针(r)时,我在asm"中得到一个错误"不可能的约束。如果我然后创建一个指向它的第二个指针(rptr),它会组装,但它会重新使用一个输入寄存器r2,它有效地覆盖了输入。 (我知道我的数组大小是32个元素,我只处理一个元素,我打算尝试循环,或者尝试加载更多的寄存器进行并行处理)

void vecSum()
{
    //two input arrays of 32 bit types, one output
    int32_t a[32];
    int32_t b[32];
    int32_t r[32];

    //initialize
    for(int cnt = 0; cnt < 32; cnt++)
    {
        a[cnt] = 0x33333333;
        b[cnt] = 0x11111111;
        r[cnt] = 0;
    }

    void *rptr = r;

    __asm__ volatile(
    "vld1.32 {d0},[%[ina]]!\n"  //load the neon register with our data at a, post increment the reg
    "vld1.32 {d1},[%[inb]]!\n"
    "vqadd.s32 d0,d1\n"        //perform the sat
    "vst1.32 d0,[%[result]]\n" //store the answer
    : [result]"=r" (rptr) /*r*/
    : [ina] "r" (a), [inb] "r" (b)
    : /*"d0", "d1", "d2"*/);

    for(int g=0; g < 32; g++)
    {
        printf("0x[%d]%x ",g,a[g]);
    }    

}

objdump的:

for(int cnt = 0; cnt < 32; cnt++)
 780:   e3530080    cmp r3, #128    ; 0x80
 784:   1afffff7    bne 768 <_Z8vecSum32v+0x28>
"vld1.32 {d1},[%[inb]]!\n"
"vqadd.s32 d0,d1\n" //perform the sat
"vst1.32 d0,[%[result]]\n"
: [result]"=r" (rptr)
: [ina] "r" (a), [inb] "r" (b)
: /*"d0", "d1", "d2"*/);
 788:   f422078f    vld1.32 {d0}, [r2]
 78c:   f421178d    vld1.32 {d1}, [r1]!
 790:   f2200011    vqadd.s32   d0, d0, d1
 794:   f402078f    vst1.32 {d0}, [r2]

总之,如果我尝试vst1.32 d0,[%[result]]其中result是数组指针r,我得到一个编译错误。如果我rptr(另一个指向r的指针)它会编译,但是使用r2(数组a)作为输出。

任何人都可以解释为什么我输出错误输出到r?为什么ptr到r是一个?

2 个答案:

答案 0 :(得分:1)

rptr应该是输入并且clobber列表中缺少"memory"时,

MSBuildWorkspace被声明为输出。

或者,您可以将数组放在结构中,并使用结构(而不​​是指针)作为asm语句的参数。

答案 1 :(得分:1)

考虑asm是否包含add %[result], %[ina], %[inb]。在那里为resultina分配r2没有任何损害。由于GCC没有分析asm语句的内容,因此它的默认假设是它包含这样的单个指令,所以如果你的更复杂,那么你需要这么说才能使事情按预期工作。

具体来说,为了防止这里有问题的重叠寄存器分配,你需要诚实地告诉你,你的asm修改了输入寄存器 - 最简单的是通过the + modifier(然后实际上使它们输出到目前为止海湾合作委员会关注)。不这样做的另一个令人不快的副作用是,编译器会认为例如r1之后仍然保留b的地址,并且可能会生成以后的代码,这些代码将因为asm实际执行的内容而出现可怕的错误。

此外,你不修改result指针,只使用它的值作为输入,所以说它是一个只写输出操作数是非常错误的。

对于r的问题,嗯,通过将其指定为输出操作数,您可以说asm将值写回该变量。除非你不能用C中的数组变量做这个(&lt; languagelawyer&gt;数组不是可修改的左值) - 你需要给asm一个变量,该变量保存数组的地址并且可以分配回来,即指针变量。 可以直接将数组用作输入操作数的原因是因为input operands are expressions而不是变量,并且计算结果的表达式会自动转换为指向该数组的第一个元素的指针(但仍然不是左值&lt; / languagelawyer&gt;)。

总而言之,对于ab的适当指针变量,此代码的合适操作数和约束看起来更像是:

: [ina] "+r" (aptr), [inb] "+r" (bptr)
: [result] "r" (r)
: "d0", "d1", "memory" /* getting clobbers right is also important */

附注:如果你只想掌握NEON指令而不是与GCC战斗,intrinsics是另一种选择。