gcc 错误:'asm'“i”中不可能的约束

yuvru6vn  于 2022-11-13  发布在  其他
关注(0)|答案(2)|浏览(338)

我在AArch 64,Linux,gcc版本是7.3.0中有一个关于内联汇编的问题

uint8x16_t vcopyq_laneq_u8_inner(uint8x16_t a, const int b, uint8x16_t c, const int d)
{
    uint8x16_t res;
    __asm__ __volatile__(
    :"ins %[dst].B[%[dlane]], %[src].B[%[sland]]    \n\t"
    :[dst] "=w"(res)
    :"0"(a), [dlane]"i"(b), [src]"w"(c), [slane]"i"(d)
    :);
    return res;
}

该函数以前是一个内联函数,可以编译并链接到一个可执行的程序中。但是现在我们想把该函数编译成一个动态库,所以我们去掉了它的inline关键字。但是它无法编译成功,错误信息为:

warning: asm operand 2 probably doesn't match constraints
warning: asm operand 4 probably doesn't match constraints
error: impossible constraint in 'asm'

我猜这个错误发生的原因是内联汇编代码“i”需要一个“立即整数操作数”,但变量“b”和“d”是常量变量,不是吗?
现在我有了一个想法来使这个函数编译成功,那就是用if-else来判断'B'和'd'的值,并用“立即整数操作数”来代替dlane/sland。但是在我们的代码中,uint8x16_t意味着一个16 uint8_t var的结构,所以我需要编写16 x16 ==256个if-else语句,这是低效的。
所以我的问题是:
1.为什么该函数可以成功地编译并链接到具有内联属性的可执行程序,而不能编译到不具有内联属性的动态链接库?
1.是否有一种有效的方法可以避免使用256 if-else语句?

dkqlctbz

dkqlctbz1#

const意味着你不能修改变量,并不是说它是一个编译时常量。只有当调用者传递了一个常量,并且你在编译时启用了优化,这样常量传播就可以将该值传递给asm语句。即使C++ constexpr在大多数上下文中并不 * 要求 * 常量表达式,它只允许常量表达式。并保证编译时常量传播是可能的。

这个函式的独立版本无法存在,但您并未将它设为static,因此编译器 * 必须 * 建立一个非内嵌定义,以便从其他编译单位呼叫,即使它内嵌至这个档案中的每个呼叫位置。但这是不可能的,因为const int b没有已知的值。

例如,

int foo(const int x){
   return x*37;
}

int bar(){
   return foo(2);
}

在为AArch 64编译的Godbolt上:注意,foo不能只返回一个常量,它需要与一个运行时变量参数一起工作,无论它的值是什么。只有在启用了优化的bar中,它才能内联,并且不需要寄存器中的x的值,只返回一个常量。(它将其用作mov的立即数)。

foo(int):
        mov     w1, 37
        mul     w0, w0, w1
        ret
bar():
        mov     w0, 74
        ret

在共享库中,您的函数也必须是__attribute__((visibility("hidden"))),这样它才能真正内联,否则符号插入的可能性意味着编译器不能假定foo(123)实际上要调用在同一个.c中定义的int foo(int)
(Or(一米十二分)
是否有一种有效的方法可以避免使用256 if-else语句?
不知道你到底在做什么,但是如果你没有一个可以处理运行时变量计数的shuffle,存储到一个16字节的数组可能是最不坏的选择。但是存储一个字节,然后重新加载整个向量会导致存储转发暂停,可能类似于x86上的成本,如果不是更糟的话。
使用AArch 64 SIMD指令高效执行算法是一个单独的问题,您还没有提供足够的信息来了解这方面的任何内容。如果您需要帮助实现某种算法以首先避免这种情况,或者使用其他shuffle进行高效的运行时变量字节插入,请询问其他问题。

gpfsuwkq

gpfsuwkq2#

约束“i”表示一个数字,一个特定的数字,它表示你希望编译器发出一条如下的指令:

ins v0.B[2], v1.B[3]

(如果我的AArch 64汇编语法不太正确,请原谅),其中v0是包含res的寄存器,v1是包含c的寄存器,2是b的值(不是包含b的寄存器的编号),3是d的值(不是包含d的寄存器的编号)。
也就是说,如果您调用

vcopyq_laneq_u8_inner(something, 2, something, 3)

函数中的指令是

ins v0.B[2], v1.B[3]

但如果你打电话

vcopyq_laneq_u8_inner(something, 1, something, 2)

函数中的指令是

ins v0.B[1], v1.B[2]

编译器必须知道bd是哪个数字,这样它就知道你需要哪条指令。如果函数是内联的,并且参数bd是常数,那么它就足够聪明了。但是,如果你用一种非内联的方式编写这个函数,编译器必须创建一个无论bd参数是多少都能工作的实际函数,如果您希望它根据它们的不同使用不同的指令,它怎么可能做到这一点呢?
唯一的方法就是编写所有256条可能的指令,并根据参数在它们之间切换。但是,编译器不会自动完成这一操作--你需要自己去做。首先,编译器不知道bd只能从0到15。
您应该考虑不将其作为库函数(这是一条指令--调用库不会增加开销吗?)或者使用不同的指令,其中通道号 * 可以 * 来自寄存器。指令ins将一个矢量元素复制到另一个矢量元素。我对ARM矢量指令不熟悉,但是应该有一些指令来根据存储在寄存器中的数字来重新排列或选择向量中的项。

相关问题