assembly 高效使用Horizontal neon 内部函数

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

从ARM指令集参考中阅读,执行水平缩减的操作确实将目标值保存在 neon 寄存器中。
然而,intrinsic定义和clang实现都将返回值转换为标量类型:

__ai uint32_t vaddvq_u32(uint32x4_t __p0) {
  uint32_t __ret;
  __ret = (uint32_t) __builtin_neon_vaddvq_u32(__p0);
  return __ret;
}

对我来说,这似乎丢失了一些有价值的信息--实现和参考指南只是隐含在所有其他位都为零的情况下,所以为了做

uint16x4_t a(uint8x8_t b) {
    return vdup_n_u16(vaddv_u8(b));
}

我希望能得到装配

addv    b0, v0.8b
   dup     v0.4h, v0.h[0]

而不是

addv    b0, v0.8b
    fmov    w8, s0
    dup     v0.4h, w8

这可能是一个错过的优化,但对我来说,这似乎也是一个设计错误,然后问题是,如果有一种方法来规避这种行为的转换到标量-或实现它在内联汇编。我所尝试的是

asm( " addv    %0.h, %0.8h " : "+w"(phase4));

但这显然是错误的,因为目标类型不是"w",这是一个无效的替换addv v30.h, v30.8h,它拒绝编译。因此,至少我缺少了向量的第一个16位元素的寄存器修饰符。

5gfr0r5j

5gfr0r5j1#

对于内联汇编方法,有template modifiers用于输出v寄存器的b/h/s/d/q名称。这个链接是针对armclang的,但它们也被主线clang和gcc支持(尽管gcc没有记录它们和doesn't seem interested in doing so)。
所以你可以

asm( " addv    %h0, %0.8h " : "+w"(phase4));

其应该发射addv h30, v30.8h
我不知道如何让编译器自己发出这个。我同意这是一个错过的优化,而且是一个相当不幸的优化,因为在许多机器上,通用和fp/simd寄存器之间的传输是昂贵的。对于Cortex A-72,fmov Wn, Sm是5个周期的延迟,dup Vn.xx, Wm是8个周期。另一方面,dup Vn.xx, Vm.y[i]只有3个周期。因此,这个错过的优化花费了我们不必要的10个延迟周期。
顺便说一句,gcc从the same missed optimization到11.x都有--甚至更糟,因为它还额外添加了一个不必要的and w0, w0, #255。但是在12.x和更高版本的it optimizes it as we wish中,将值保存在向量寄存器中。

相关问题