2012-05-09 16 views
17

Podczas korzystania z instrukcji SSE2, takich jak PADDD (tj. Wewnętrznie _mm_add_epi32), istnieje sposób sprawdzenia, czy któraś z operacji została przepełniona?Kontrola przekroczenia liczby całkowitej SSE2

Pomyślałem, że być może flaga w rejestrze kontrolnym MXCSR może zostać ustawiona po przepełnieniu, ale nie widzę, żeby to się stało. Na przykład, _mm_getcsr() drukuje taką samą wartość w obu przypadkach wymienionych poniżej (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; 
} 

Czy jest jakiś inny sposób na sprawdzenie przepełnienia z SSE2?

+3

można powtórzyć obliczenia w trybie nasycenia (PADDS) i porównać wyniki. –

+2

Czy jesteś zainteresowany podpisanym lub niepodpisanym przepełnieniem? –

+3

@Dmitri: nie ma nasycenia 32-bitowego w SSE2 (tylko 8-bitowe i 16-bitowe). –

Odpowiedz

10

Oto nieco bardziej wydajne wersja @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) 
} 

Używa wyrażenia wykrywania przepełnienia z Hacker's Delight stronie 27:

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

nocie, że wektor przelewowy będzie zawierać więcej konwencjonalne wartości logiczne SIMD równe -1 dla TRUE (przepełnienie) i 0 dla FALSE (brak przepełnienia). Jeśli potrzebujesz tylko przepełnienia w bicie znaku, a pozostałe bity to "nie obchodzi", wówczas możesz pominąć ostatni wiersz funkcji, zmniejszając liczbę instrukcji SIMD z 5 do 4.

NB: to rozwiązanie, a także previous solution on which it is based są dla wartości całkowitych ze znakiem. Rozwiązanie dla wartości niepodpisanych będzie wymagało nieco innego podejścia (patrz @Stephen Canon 's answer).

+1

Dzięki! To jakieś fajne sztuczki, szczególnie. prawą zmianę, aby zreplikować bit znaku. –

+1

Warto podkreślić to rozwiązanie tylko dla wartości SIGNED - nie UNSIGNED. – SquareRootOfTwentyThree

+0

@SquareRootOfTwentyThree: dzięki - powinno to wynikać ze wszystkich odniesień do znaku bitowego itp., Ale dodam kolejne zdanie tylko po to, aby było w 100% jasne. –

2

Żadne flagi nie są dotykane przez podstawową instrukcję PADDD.

Aby to przetestować, musisz napisać dodatkowy kod, w zależności od tego, co chcesz zrobić.

Uwaga: są nieco utrudnione przez brak epi32 intrisics

9

Skoro masz 4 możliwych przepełnienia, rejestr sterujący będzie bardzo szybko zabraknie bitów, zwłaszcza, jeśli chcesz carrys, znak itp i nawet w przypadku dodawania wektora składającego się z 16 bajtów :-)

Flaga przekroczenia jest ustawiona, jeżeli bity znaku wejściowego są równe, a bit znaku wynikowego jest różny od bitu znaku wejściowego. Ta funkcja oblicza sum = a+b i przepełnia się ręcznie. Dla każdego przepełnienia 0x80000000 jest returend w 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) 
} 

Uwaga: Jeśli nie masz gcc, trzeba wymienić ^&+ operatorom przez odpowiednie intrinsics SSE, jak _mm_and_si128(), _mm_add_epi32() itp

Edit: Właśnie zauważyłem and z Maskę można oczywiście zrobić na samym końcu funkcji, oszczędzając dwie operacje and. Ale kompilator najprawdopodobniej będzie wystarczająco inteligentny, aby zrobić to sam.

+0

Dzięki, drhirsch! Zaakceptowałem odpowiedź Paula R, ponieważ jest to najbardziej sensowne do podkreślenia dla potomności, ale twoja odpowiedź była bardzo pomocna i doceniam czas poświęcony na jej napisanie. –

4

Zauważyłem, że prosiłeś o rozwiązanie również dla niepodpisanego; Na szczęście, to całkiem łatwe:

__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)); 

Normalnie wykryć niepodpisane przepełnienie, wystarczy sprawdzić albo sum < a lub sum < b. Jednak SSE nie ma niepodpisanych porównań; xor -s argumenty z 0x80000000 pozwala użyć podpisanego porównania, aby uzyskać ten sam wynik.

+1

AVX512 wreszcie dodaje niepodpisane porównania: ['_mm512_cmp [eq | ge | gt | le | lt | neq] _epu32_mask' (https://github.com/HJLebbink/asm-dude/wiki/VPCMPD_VPCMPUD). Pełen zakres rozmiarów elementów 8/16/32/64 bitowych jest dostępny dla podpisanego i niepodpisanego ('epi' vs.' epu'), z arbitralnym porównawczym predykatem zamiast po prostu 'gt' i' eq' dla AVX2 i wcześniejszych liczba całkowita porównywana. –

+1

@PeterCordes * wreszcie *! –

Powiązane problemy