2012-05-09 9 views
17

Al usar instrucciones SSE2 como PADDD (es decir, el _mm_add_epi32 intrínseco), ¿hay alguna forma de comprobar si alguna de las operaciones se desbordó?Comprobación de desbordamiento de entero SSE2

Pensé que tal vez una bandera en el registro de control MXCSR podría establecerse después de un desbordamiento, pero no veo que eso suceda. Por ejemplo, _mm_getcsr() imprime el mismo valor en ambos casos por debajo (8064):

#include <iostream> 
#include <emmintrin.h> 

using namespace std; 

void main() 
{ 
    __m128i a = _mm_set_epi32(1, 0, 0, 0); 
    __m128i b = _mm_add_epi32(a, a); 
    cout << "MXCSR: " << _mm_getcsr() << endl; 
    cout << "Result: " << b.m128i_i32[3] << endl; 

    __m128i c = _mm_set_epi32((1<<31)-1, 3, 2, 1); 
    __m128i d = _mm_add_epi32(c, c); 
    cout << "MXCSR: " << _mm_getcsr() << endl; 
    cout << "Result: " << d.m128i_i32[3] << endl; 
} 

¿Hay alguna otra manera de comprobar si hay desbordamiento con SSE2?

+3

puede repetir el cálculo en modo de saturación (PADDS) y comparar los resultados. –

+2

¿Está interesado en el desbordamiento firmado o sin firmar? –

+3

@Dmitri: no hay un agregado de saturación de 32 bits en SSE2 (solo 8 bit y 16 bit) –

Respuesta

10

Aquí es una versión algo más eficiente de @hirschhornsalz'ssum_and_overflow function:

void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) 
{ 
    __v4si sa, sb; 

    sum = _mm_add_epi32(a, b);     // calculate sum 
    sa = _mm_xor_si128(sum, a);     // compare sign of sum with sign of a 
    sb = _mm_xor_si128(sum, b);     // compare sign of sum with sign of b 
    overflow = _mm_and_si128(sa, sb);   // get overflow in sign bit 
    overflow = _mm_srai_epi32(overflow, 31); // convert to SIMD boolean (-1 == TRUE, 0 == FALSE) 
} 

Se utiliza una expresión para detección de desbordamiento de Hacker's Delight página 27:

sum = a + b; 
overflow = (sum^a) & (sum^b);    // overflow flag in sign bit 

Tenga en cuenta que el vector de desbordamiento contendrá más valores booleanos SIMD convencionales de -1 para VERDADERO (desbordamiento) y 0 para FALSO (sin desbordamiento). Si solo necesita el desbordamiento en el bit de signo y los demás bits son "no me importa", puede omitir la última línea de la función, reduciendo el número de instrucciones SIMD de 5 a 4.

NB: this solución, así como previous solution on which it is based son valores enteros con signo. Una solución para valores sin firmar requerirá un enfoque ligeramente diferente (vea @Stephen Canon 's answer).

+1

¡Gracias! Esos son algunos trucos agradables, esp. el cambio correcto para replicar el bit de signo. –

+1

Vale la pena destacar que esta solución es válida solo para valores FIRMADOS, no SIN FIRMAR. – SquareRootOfTwentyThree

+0

@SquareRootOfTwentyThree: gracias, eso debería ser evidente a partir de todas las referencias al bit de signo, etc., pero agregaré una oración adicional para que quede 100% claro. –

2

La instrucción PADDD subyacente no toca ninguna marca.

Para probar esto, tiene que escribir un código adicional, dependiendo de lo que quiera hacer.

Nota: Usted es un poco obstaculizado por la falta de epi32 intrisics

9

Puesto que usted tiene 4 posibles desbordamientos, el registro de control podría correr muy rápidamente fuera de bits, sobre todo, si usted quiere carrys, señal, etc y que incluso para una adición vectorial que consta de 16 bytes :-)

El indicador de desbordamiento se establece, si los bits del signo de entrada son iguales y el bit del signo de resultado es diferente de un bit de signo de entrada.

Esta función calcula sum = a+b y desbordamiento manualmente. Por cada desbordamiento 0x80000000 es returend en overflow.

void sum_and_overflow(__v4si a, __v4si b, __v4si& sum, __v4si& overflow) { 
    __v4si signmask = _mm_set1_epi32(0x80000000); 
    sum = a+b; 
    a &= signmask; 
    b &= signmask; 
    overflow = sum & signmask; 
    overflow = ~(a^b) & (overflow^a); // overflow is 1 if (a==b) and (resultbit has changed) 
} 

Nota: Si usted no tiene gcc, se tenga que sustituir los ^&+ operadores por las características intrínsecas de la ESS apropiadas, como _mm_and_si128(), etc. _mm_add_epi32()

Editar: Acabo de notar la and con la máscara, por supuesto, puede hacerse al final de la función, guardando dos operaciones and. Pero es muy probable que el compilador sea lo suficientemente inteligente como para hacerlo solo.

+0

Gracias, drhirsch! Acepté la respuesta de Paul R, ya que es la que tiene más sentido destacar para la posteridad, pero tu respuesta fue muy útil y agradezco el tiempo que tardaste en escribirla. –

4

Me di cuenta de que también había pedido una solución para unsigned; Afortunadamente, eso es bastante fácil también:

__v4si mask = _mm_set1_epi32(0x80000000); 
sum = _mm_add_epi32(a, b); 
overflow = _mm_cmpgt_epi32(_mm_xor_si128(mask, a), _mm_xor_si128(mask, sum)); 

Normalmente para detectar desbordamiento sin firmar, sólo tiene que comprobar ya sea sum < a o sum < b. Sin embargo, SSE no tiene comparaciones sin firmar; xor -siguiendo los argumentos con 0x80000000 le permite usar una comparación firmada para obtener el mismo resultado.

+1

AVX512 finalmente agrega comparaciones sin signo: ['_mm512_cmp [eq | ge | gt | le | lt | neq] _epu32_mask'] (https://github.com/HJLebbink/asm-dude/wiki/VPCMPD_VPCMPUD). La gama completa de tamaños de elementos de 8/16/32/64 bits está disponible para firmado y sin signo ('epi' contra' epu'), con un predicado de comparación arbitrario en lugar de 'gt' y' eq' para AVX2 y versiones anteriores entero se compara –

+1

@PeterCordes * finalmente *! –

Cuestiones relacionadas