高效使用水平 Neon 内在函数

问题描述 投票:0回答:1

从 ARM Instruction Set Reference 中读取,执行水平归约的操作确实将目标值保存在 neon 寄存器中。

但是,内在定义和 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 位第一个元素的寄存器修饰符。

assembly inline-assembly arm64 intrinsics neon
1个回答
2
投票

对于内联汇编方法,有模板修饰符来输出

b/h/s/d/q
寄存器的
v
名称。该链接适用于 armclang,但主线 clang 和 gcc 也支持它们(尽管尴尬的是 gcc 没有记录它们并且似乎对此不感兴趣)。

所以你可以做

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 在 11.x 之前有相同的优化缺失——更糟糕的是因为它增加了一个额外的不必要的

and w0, w0, #255
。但在 12.x 及更高版本中它会按照我们的意愿对其进行优化,将值保留在向量寄存器中。

© www.soinside.com 2019 - 2024. All rights reserved.