ARM 汇编向量加法

问题描述 投票:0回答:1

我必须使用内联ARM汇编在C++程序中实现向量加法。

我写了这段代码:

#include <iostream>
#include <stdio.h>
#include <arm_neon.h>

using namespace std;

int main(){
float v1[4] = {1.0f, 2.1f, -3.1f, 2.5f};
float v2[4] = {2.0f, 1.0f, 1.1f, -2.5f};
float result[4] = { };

asm(
"ldr q31, [%[vec1]]\n"
"ldr q30, [%[vec2]]\n"
"FADD v31.4S, v31.4S, v30.4S\n"
"str q31, [%[r]]\n"
:[r]"=r"(result): [vec1]"r"(&v1), [vec2]"r"(&v2)
);

for (float i: result) cout << " " << i;
cout << "\n";
}

但结果是这样的: -3.33452e+38 9.18341e-41 -2.23081e+25 9.18341e-41

我真的是装配新手。 我的代码中的问题在哪里以及如何解决它们?谢谢。

assembly inline-assembly arm64 neon
1个回答
1
投票

让我在前面加上一个重要的警告:正确使用 GCC 内联汇编很难,尤其是对于初学者而言。默认建议是不要使用它https://stackoverflow.com/tags/inline-assembly/info 上有一些更通用的资源,但如果可能的话,我会先编写独立的汇编函数(在它们自己的 .s 文件中)。如果你从内联汇编开始,你基本上将自己置于必须学习汇编语言的位置 simultaneously 与高级(和文档很少)编译器设计。

也就是说,您的代码对于初学者来说非常好,因为它的六行代码中只有三个错误。错误如下:

  • result
    操作数实际上是一个input,而不是输出。虽然您要将数据存储在数组中,但 asm 块的操作数是数组的address。换句话说,无论分配给该操作数的寄存器是什么(我构建它时恰好是 x0),您都需要编译器在执行 asm 块之前用
    result
    的地址填充它。如果它是一个输出,你告诉编译器你不关心块之前那个寄存器中的内容,但它的值应该在之后存储到 result
     中。所以这意味着,就目前而言,
    str q31, [%[r]]
     正在将数据存储到一个完全随机的地址;你很幸运它没有崩溃或损坏数据。

    (实际上,在这种情况下发生的事情是编译器认为

    vec1

     只需要作为输入,而 
    result
     只需要作为输出,默认情况下它假设输入在输出产生之前被消耗,所以它分配给他们
    both注册x0。因此你的结果实际上被存储回vec1
    ,并且
    result
    的内容仍然是未初始化的垃圾,这就是打印出来的内容。)

  • 当你在内联汇编中引用显式寄存器时,就像这里的

    q30, q31

    ,你必须将它们声明为“clobbered”;否则编译器可能会在其中保留重要数据。

    实际上,除非绝对必要,否则通常不会引用显式寄存器,但会适当地声明操作数,以便编译器为您选择它们。同样,您通常不会在 asm 块中进行自己的加载和存储;你让你的操作数成为数据的

    contents而不是它们的地址,然后编译器为你加载和存储。但如果你刚刚开始也没关系。

  • 如果您的 asm 读取或写入不属于显式操作数的内存,则需要包含一个

    memory

     clobber。这里看起来 
    vec1
    vec2
     是明确的操作数,但这些数组的 
    contents 不是操作数,只是它们的 addresses。如您所见,这变得非常微妙。有一些方法可以使用 m
     约束来处理这个问题,请参阅
    How can I indicate that the memory *pointed* to by an inline ASM argument may be used?了解更多详细信息,但对于非专家来说更安全使用 memory
     clobber.

所以固定版本看起来像:

asm("ldr q31, [%[vec1]]\n" "ldr q30, [%[vec2]]\n" "FADD v31.4S, v31.4S, v30.4S\n" "str q31, [%[r]]\n" : // no outputs : [r]"r"(result), [vec1]"r"(&v1), [vec2]"r"(&v2) : "q30", "q31", "memory");
    
© www.soinside.com 2019 - 2024. All rights reserved.