有没有办法使用MMX / SSE减去x86上饱和的压缩无符号双字?

时间:2019-06-10 12:06:15

标签: assembly x86 sse mmx saturation-arithmetic

我一直在看MMX / SSE,我想知道。对于无符号字节和字(而非双字),有打包,饱和减法的说明。

有没有一种方法可以做我想要的事情,如果没有,为什么没有呢?

1 个答案:

答案 0 :(得分:2)

如果您有可用的SSE4.1,我认为您不会比使用@harold建议的pmaxud + psubd方法更好。借助AVX2,您当然也可以使用相应的256位变体。

__m128i subs_epu32_sse4(__m128i a, __m128i b){
    __m128i mx = _mm_max_epu32(a,b);
    return _mm_sub_epi32(mx, b);
}

没有SSE4.1,您需要以某种方式比较两个参数。不幸的是,没有epu32比较(不是在AVX512之前),但是您可以通过首先在两个参数中添加0x80000000(在这种情况下,这相当于异或)来模拟一个:

__m128i cmpgt_epu32(__m128i a, __m128i b) {
    const __m128i highest = _mm_set1_epi32(0x80000000);
    return _mm_cmpgt_epi32(_mm_xor_si128(a,highest),_mm_xor_si128(b,highest));
}

__m128i subs_epu32(__m128i a, __m128i b){
    __m128i not_saturated = cmpgt_epu32(a,b);
    return _mm_and_si128(not_saturated, _mm_sub_epi32(a,b));
}

在某些情况下,可能最好用最高位的一些位捻代替比较,然后使用移位将其广播到每一位(这代替了pcmpgtd和通过0x80000000执行三个位逻辑运算(并且必须至少加载一次psrad一次)和五个位逻辑运算):

__m128i subs_epu32_(__m128i a, __m128i b) {
    __m128i r = _mm_sub_epi32(a,b);
    __m128i c = (~a & b) | (r & ~(a^b)); // works with gcc/clang. Replace by corresponding intrinsics, if necessary (note that `andnot` is a single instruction)
    return _mm_srai_epi32(c,31) & r;
}

Godbolt-Link,还包括adds_epu32个变体:https://godbolt.org/z/n4qaW1 奇怪的是,对于非SSE4.1变体,clang需要比gcc更多的寄存器副本。另一方面,当使用SSE4.1编译时,clang为pmaxud变体找到cmpgt_epu32优化:https://godbolt.org/z/3o5KCm