assembly ARM汇编向量加法

p5fdfcr1  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(175)

我必须在C++程序中使用内联ARM汇编实现向量加法。
我写了这段代码:

#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. 18341 e-41
我真的是新来的组装。我的代码中的问题在哪里以及如何修复它们?谢谢你。

lymgl2op

lymgl2op1#

让我在前面加上一个大大的警告:正确掌握GCC内联asm很难,尤其是对于初学者。默认建议为don't use ithttps://stackoverflow.com/tags/inline-assembly/info上有一些更通用的资源,但如果可能的话,我会从编写独立的汇编函数(在它们自己的.s文件中)开始。如果你从内联汇编开始,你基本上把自己放在了必须学习汇编语言的位置,同时学习高级(和缺乏文档的)编译器设计。
也就是说,您的代码对于初学者来说非常好,因为它的六行代码中只有三个bug。这些bug如下:

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

(实际上,在这个例子中发生的是编译器认为vec1只需要作为输入,result只需要作为输出,默认情况下,它假设输入在产生输出之前被消耗,所以它将它们 * 都 * 分配给寄存器x 0。因此,您的结果实际上被存储回vec1,而result的内容仍然是未初始化的垃圾,这就是打印出来的内容。)

  • 当你在内联asm中引用显式寄存器时,比如这里的q30, q31,你必须将它们声明为“clobbered”;否则编译器可能会在其中保存重要数据。

在实践中,除非绝对必要,否则通常不会引用显式寄存器,但可以适当地声明操作数,以便编译器为您选择它们。同样,您通常不会在asm块中执行自己的加载和存储;你把你的操作数变成数据的内容而不是它们的地址,然后编译器为你加载和存储。不过,如果你才刚开始也没关系。

  • 如果你的asm读写的内存不是显式操作数的一部分,你需要包括一个memory clobber。这里看起来像vec1vec2是显式操作数,但这些数组的 * 内容 * 不是操作数,只是它们的 * 地址 *。正如你所看到的,这变得非常微妙。有一些方法可以使用m约束来处理这个问题,更多细节请参见How can I indicate that the memory pointed to by an inline ASM argument may be used?,但对于非Maven来说,使用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");

相关问题