我在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语句?
2条答案
按热度按时间dkqlctbz1#
const
意味着你不能修改变量,并不是说它是一个编译时常量。只有当调用者传递了一个常量,并且你在编译时启用了优化,这样常量传播就可以将该值传递给asm语句。即使C++constexpr
在大多数上下文中并不 * 要求 * 常量表达式,它只允许常量表达式。并保证编译时常量传播是可能的。这个函式的独立版本无法存在,但您并未将它设为
static
,因此编译器 * 必须 * 建立一个非内嵌定义,以便从其他编译单位呼叫,即使它内嵌至这个档案中的每个呼叫位置。但这是不可能的,因为const int b
没有已知的值。例如,
在为AArch 64编译的Godbolt上:注意,
foo
不能只返回一个常量,它需要与一个运行时变量参数一起工作,无论它的值是什么。只有在启用了优化的bar
中,它才能内联,并且不需要寄存器中的x
的值,只返回一个常量。(它将其用作mov
的立即数)。在共享库中,您的函数也必须是
__attribute__((visibility("hidden")))
,这样它才能真正内联,否则符号插入的可能性意味着编译器不能假定foo(123)
实际上要调用在同一个.c
中定义的int foo(int)
(Or(一米十二分)
是否有一种有效的方法可以避免使用256 if-else语句?
不知道你到底在做什么,但是如果你没有一个可以处理运行时变量计数的shuffle,存储到一个16字节的数组可能是最不坏的选择。但是存储一个字节,然后重新加载整个向量会导致存储转发暂停,可能类似于x86上的成本,如果不是更糟的话。
使用AArch 64 SIMD指令高效执行算法是一个单独的问题,您还没有提供足够的信息来了解这方面的任何内容。如果您需要帮助实现某种算法以首先避免这种情况,或者使用其他shuffle进行高效的运行时变量字节插入,请询问其他问题。
gpfsuwkq2#
约束“i”表示一个数字,一个特定的数字,它表示你希望编译器发出一条如下的指令:
(如果我的AArch 64汇编语法不太正确,请原谅),其中v0是包含
res
的寄存器,v1是包含c
的寄存器,2是b
的值(不是包含b
的寄存器的编号),3是d
的值(不是包含d
的寄存器的编号)。也就是说,如果您调用
函数中的指令是
但如果你打电话
函数中的指令是
编译器必须知道
b
和d
是哪个数字,这样它就知道你需要哪条指令。如果函数是内联的,并且参数b
和d
是常数,那么它就足够聪明了。但是,如果你用一种非内联的方式编写这个函数,编译器必须创建一个无论b
和d
参数是多少都能工作的实际函数,如果您希望它根据它们的不同使用不同的指令,它怎么可能做到这一点呢?唯一的方法就是编写所有256条可能的指令,并根据参数在它们之间切换。但是,编译器不会自动完成这一操作--你需要自己去做。首先,编译器不知道
b
和d
只能从0到15。您应该考虑不将其作为库函数(这是一条指令--调用库不会增加开销吗?)或者使用不同的指令,其中通道号 * 可以 * 来自寄存器。指令
ins
将一个矢量元素复制到另一个矢量元素。我对ARM矢量指令不熟悉,但是应该有一些指令来根据存储在寄存器中的数字来重新排列或选择向量中的项。