我正在尝试使用
vfmlalq_low_f16
和 vfmlalq_high_f16
内在函数(对应于 FMLAL 和 FMLAL2 指令),但我观察到的行为似乎没有意义。
需要一个
float32x4
和两个 float16x8
寄存器,从文档中他们应该从两个 fp16 寄存器中选择低 4 个值或高 4 个值,将它们隐藏到 fp32,按组件相乘并累加结果在 fp32 寄存器中。
因此,调用
vfmlalq_low_f16(r, a, b)
应该使用 fp32 为 r[i] += a[i] * b[i]
计算 0 < i < 4
;高版本应该做r[i] += a[i + 4] * b[i + 4]
。
我的问题是,无论我在开始时放入三个寄存器中,结果向量中的值绝对没有变化。
根据我的理解,在我的 Macbook M1 上编译并运行以下代码应该可以工作:
int main(void) {
float32x4_t l = vdupq_n_f32(1);
float32x4_t h = vdupq_n_f32(1);
float16x8_t a = vdupq_n_f16(2);
float16x8_t b = vdupq_n_f16(3);
dump_f32("l", l);
dump_f32("h", h);
dump_f16("a", a);
dump_f16("b", b);
vfmlalq_low_f16 (l, a, b);
vfmlalq_high_f16(h, a, b);
dump_f32("l", l);
dump_f32("h", h);
}
运行时显示:
l = [ 1.000000 1.000000 1.000000 1.000000 ]
h = [ 1.000000 1.000000 1.000000 1.000000 ]
a = [ 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 2.000000 ]
b = [ 3.000000 3.000000 3.000000 3.000000 3.000000 3.000000 3.000000 3.000000 ]
l = [ 1.000000 1.000000 1.000000 1.000000 ]
h = [ 1.000000 1.000000 1.000000 1.000000 ]
无论我对
a
和 b
输入尝试什么,l 和 h 中的值都不会改变。我是否错误地理解了说明?
内在函数返回一个结果,您需要将其分配给变量。
在 C 术语中,源操作数是按值,而不是像
&h
那样按引用。
h = vfmlalq_high_f16(h, a, b);
与 asm 指令不同,
vfmlalq_high_f16
的第一个源操作数是只读的,因为如果您想不修改 mov
并将结果分配到其他地方,高级语言编译器可以为您发明 h
指令。
机器指令在机器代码中的寄存器编号空间有限,因此 3 输入指令通常重用第一个输入作为输出。 但这对于高级语言来说不是问题,因此您总是有一个返回值和按值而不是按引用获取的只读源操作数。 所以他们可以在 C 和 C++ 中工作,而无需编写
vfmlalq_high_f16( &h, a, b);
(某些 32 位模式 ARM NEON 洗牌会写入两个向量结果,例如
vzip
。ARM 通过让 内在 返回 int32x4x2_t
(一对向量)来处理该结果。因此,即使在那里,它们也会避免获取输入操作数参考。)
换句话说,你写了相当于
的东西h + a*b;
而不是
h += a*b;