Assembly SSE4.1带溢出的无符号整数比较
考虑到16位无符号加法(Assembly SSE4.1带溢出的无符号整数比较,assembly,x86,simd,sse2,sse4,Assembly,X86,Simd,Sse2,Sse4,考虑到16位无符号加法(\u mm\u add\u epi16())可能会溢出,是否有任何方法执行类似于C>=(a+B)与SSE2/4.1指令的比较 代码片段如下所示- #define _mm_cmpge_epu16(a, b) _mm_cmpeq_epi16(_mm_max_epu16(a, b), a) __m128i *a = (__m128i *)&ptr1; __m128i *b = (__m128i *)&ptr2; __m128i *c = (__m128i *)
\u mm\u add\u epi16()
)可能会溢出,是否有任何方法执行类似于C>=(a+B)与SSE2/4.1指令的比较
代码片段如下所示-
#define _mm_cmpge_epu16(a, b) _mm_cmpeq_epi16(_mm_max_epu16(a, b), a)
__m128i *a = (__m128i *)&ptr1;
__m128i *b = (__m128i *)&ptr2;
__m128i *c = (__m128i *)&ptr3;
_m128i xa = _mm_lddqu_si128(a);
_m128i xb = _mm_lddqu_si128(b);
_m128i xc = _mm_lddqu_si128(c);
_m128i res = _mm_add_epi16(xa, xb);
_m128i xmm3 = _mm_cmpge_epu16(xc, res);
问题在于,当16位加法溢出(环绕)时,大于比较会导致误报。我不能用饱和加法来达到我的目的。我在这里研究了检测无符号加法溢出的机制。但是如何使用if进行大于比较。以下是一些合理的方法:
#include <cstdint>
using v8u16 = uint16_t __attribute__((vector_size(16)));
v8u16 lthsum1(v8u16 a, v8u16 b, v8u16 c) {
return (c >= a) & (c - a >= b);
}
v8u16 lthsum2(v8u16 a, v8u16 b, v8u16 c) {
return (a + b >= a) & (a + b <= c);
}
#包括
使用v8u16=uint16\u t\u属性(向量大小(16));
v8u16 lthsum1(v8u16 a、v8u16 b、v8u16 c){
返回(c>=a)和(c-a>=b);
}
v8u16 lthsum2(v8u16 a、v8u16 b、v8u16 c){
return(a+b>=a)&(a+b根据指令集中的可用信息构建缺少的原语。
这里有一个可能的实现,未经测试
我认为你应该首先根据你链接的问题检查溢出。如果你检测到溢出,你就知道C>(A+B)
是假的。否则,下一步检查。因为你在做向量,你可能必须执行这两个检查,并使用位运算合并它们。(编辑以修复反向条件).你想检查C>(A+B)
还是C>=(A+B)
?在第一种情况下,我看不出饱和加法是如何导致误报的。编辑-其C>=(A+B)我认为C-A>=B
(使用饱和减法)应该有效(未经测试)。编辑:否(需要进一步考虑)@彼得命令是的,C-A>=B
如果B>0
或C>=A
中的一个得到保证,那么C-A>=B
技巧就会起作用。(当然,与C-B>=A
类似)。可以检查C-min(A,B)>=max(A,B)
如果我计算正确的话,这将是5 uops。当c为0xFFFF且a+b溢出时,这只是一个需要处理的角落案例。@Kaustubh你是什么意思?谢谢。这是我测试中的一个问题。这是我的验收答案。
// Compare uint16_t lanes for a >= b
inline __m128i cmpge_epu16( __m128i a, __m128i b )
{
const __m128i max = _mm_max_epu16( a, b );
return _mm_cmpeq_epi16( max, a );
}
// Compare uint16_t lanes for c >= a + b, with overflow handling
__m128i cmpgeSum( __m128i a, __m128i b, __m128i c )
{
// Compute c >= a + b, ignoring overflow issues
const __m128i sum = _mm_add_epi16( a, b );
const __m128i ge = cmpge_epu16( c, sum );
// Detect overflow of a + b
const __m128i sumSaturated = _mm_adds_epu16( a, b );
const __m128i sumInRange = _mm_cmpeq_epi16( sum, sumSaturated );
// Combine the two
return _mm_and_si128( ge, sumInRange );
}