您可能想查看 SIMDe 的实现_mm_madd_epi16和_mm_maddubs_epi16(请未来的读者注意:您可能想检查这些文件的最新版本,因为 SIMDe 中的实现有时会得到改进,而且我不太可能记得更新此答案)。这些实现只是从那里复制的。
如果您使用的是 AArch64,因为_mm_madd_epi16您可能想使用vmull_s16+vget_low_s16表示低半部分,使用 avmull_high_s16表示高半部分,然后使用vpaddq_s32将它们相加成 128 位结果。如果没有 AArch64,您将需要两次vmull_s16调用(一个带有vget_low_s16,一个带有vget_high_s16),但由于vpaddq_s32不支持,您将需要两次vpadd_s32带有 a 的调用vcombine_s32:
#if defined(SIMDE_ARM_NEON_A64V8_NATIVE)
int32x4_t pl = vmull_s16(vget_low_s16(a_.neon_i16), vget_low_s16(b_.neon_i16));
int32x4_t ph = vmull_high_s16(a_.neon_i16, b_.neon_i16);
r_.neon_s32 = vpaddq_s32(pl, ph);
#elif defined(SIMDE_ARM_NEON_A32V7_NATIVE)
int32x4_t pl = vmull_s16(vget_low_s16(a_.neon_i16), vget_low_s16(b_.neon_i16));
int32x4_t ph = vmull_s16(vget_high_s16(a_.neon_i16), vget_high_s16(b_.neon_i16));
int32x2_t rl = vpadd_s32(vget_low_s32(pl), vget_high_s32(pl));
int32x2_t rh = vpadd_s32(vget_low_s32(ph), vget_high_s32(ph));
r_.neon_i32 = vcombine_s32(rl, rh);
#endif
Run Code Online (Sandbox Code Playgroud)
因为_mm_maddubs_epi16它有点复杂,但我认为 AArch64 特定的版本不会有多大作用:
/* Zero extend a */
int16x8_t a_odd = vreinterpretq_s16_u16(vshrq_n_u16(a_.neon_u16, 8));
int16x8_t a_even = vreinterpretq_s16_u16(vbicq_u16(a_.neon_u16, vdupq_n_u16(0xff00)));
/* Sign extend by shifting left then shifting right. */
int16x8_t b_even = vshrq_n_s16(vshlq_n_s16(b_.neon_i16, 8), 8);
int16x8_t b_odd = vshrq_n_s16(b_.neon_i16, 8);
/* multiply */
int16x8_t prod1 = vmulq_s16(a_even, b_even);
int16x8_t prod2 = vmulq_s16(a_odd, b_odd);
/* saturated add */
r_.neon_i16 = vqaddq_s16(prod1, prod2);
Run Code Online (Sandbox Code Playgroud)