您可能想查看_mm_madd_epi16 和 _mm_maddubs_epi16 的 SIMDe 实现(请注意未来的读者:您可能需要检查这些文件的最新版本,因为 SIMDe 中的实现有时会得到改进,而且我不太可能会记得更新这个答案)。这些实现只是从那里复制过来的。
如果您在 AArch64 上,对于 _mm_madd_epi16,您可能希望使用 vmull_s16+vget_low_s16 作为低半部分,vmull_high_s16 作为高半部分,然后使用 vpaddq_s32 将它们加在一起成 128 位结果。如果没有 AArch64,您将需要两个 vmull_s16 调用(一个带有 vget_low_s16,一个带有 vget_high_s16),但由于不支持 vpaddq_s32,您需要两个带有 vcombine_s32 的 vpadd_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
对于_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);