SSE: rozszerzenie SIMD wprowadzone w procesorach Intel Pentium III i AMD AthlonXP, znane też jako Katmai New Instructions (KNI). Wspierane od Windows98 i Linuxa z jądrem 2.2.
Osiem nowych 128-bitowych rejestrów, podzielonych na cztery 32-bitowe
elementy zmiennopozycyjne pojedynczej precyzji, nazwanych
xmm0-xmm7
. Rejestr sterujący mxcsr
.
Mnóstwo nowych instrukcji operujących na tych rejestrach (a niektóre także na rejestrach MMX i rejestrach uniwersalnych. Wybór prezentowanych jest mocno fragmentaryczny.
movaps
- wymaga wyrównanego adresu;
movups
- adres nie musi być wyrównany.
movss
- przepisuje 32-bitową liczbę zmiennopozycyjną
między rejestrem XMM a pamięcią lub innym rejestrem XMM.
movsd
- przepisuje 64-bitową liczbę zmiennopozycyjną
między rejestrem XMM a pamięcią lub innym rejestrem XMM.
movhps
- do górnej połówki rejestru xmm
.
movlps
- do dolnej połówki rejestru xmm
.
movhlps
- z górnej połówki rejestru źródłowego do dolnej
połówki rejestru docelowego.
movlhps
- z dolnej połówki rejestru źródłowego do górnej
połówki rejestru docelowego.
Tasowanie (shuffling) służy do zmiany kolejności elementów
w pojedynczym rejestrze SSE lub do pomieszania wartości z dwóch takich
rejestrów. Argumentami instrukcji shufps
są dwa rejestry
SSE i 8-bitowa maska. Pierwsze dwa elementy rejestru docelowego są
nadpisywane dowolnymi dwoma elementami tego rejestru. Trzeci i czwarty
element są nadpisywane dwoma elementami rejestru źródłowego.
Wyborem elementów sterują kolejne pary bitów maski, traktowane jako
liczby w zakresie 0-3.
Przykłady:
shufps xmm0,xmm0,0x1b ;odwraca kolejność elementów (0x1B=00 01 10 11) shufps xmm0,xmm0,0xaa ;cztery kopie trzeciego elementu (0xAA=10 10 10 10)
Instrukcje shufps
i shufpd
operują na
spakowanych liczbach zmiennopozycyjnych. Dla zwykłych wartości są
instrukcje pshufb
, pshufw
i pshufd
.
pshufw
- przerzuca wartości z rejestru źródłowego (lub
pamięci) do docelowego używając maski.
Przykład na policzenie iloczynu (cross product), aby znaleźć wektor prostopadły do dwóch wektorów.
;; float *cross_product (float V1[4], float V2[4], float W[4]) ;; Find the cross product of two constant vectors and return it. ;; W.x = V1.y * V2.z - V1.z * V2.y ;; W.y = V1.z * V2.x - V1.x * V2.z ;; W.z = V1.x * V2.y - V1.y * V2.x global cross_product section .text cross_product: push ebp mov ebp,esp mov eax,[ebp+8] ;Adresy argumentów do rejestrów mov ebx,[ebp+12] movups xmm0,[eax] ;Jeśli wyrównane to movaps movups xmm1,[ebx] movaps xmm2,xmm0 ;Kopie movaps xmm3,xmm1 shufps xmm0,xmm0,0xd8 ;Zamieniamy miejscami 2 i 3 element (V1) shufps xmm1,xmm1,0xe1 ;Zamieniamy miejscami 1 i 2 element (V2) mulps xmm0,xmm1 shufps xmm2,xmm2,0xe1 ;Zamieniamy miejscami 1 i 2 element (V1) shufps xmm3,xmm3,0xd8 ;Zamieniamy miejscami 2 i 3 element (V2) mulps xmm2,xmm3 subps xmm0,xmm2 mov eax,[ebp+16] movups [eax],xmm0 ;Wynik pop ebp ret
Podstawowe operacje arytmetyczne mają dwa warianty, rozróżniane przyrostkami:
ss
: instrukcja operuje tylko na dolnym elemencie,
pozostałe nie ulegają zmienie;
ps
: instrukcja operuje na wszystkich elementach
równocześnie.
Argumenty dla operacji zmiennopozycyjnych nie mogą być stałymi, dozwolone są tylko rejestry i komórki pamięci (adresy).
Dodawanie: addss
, addps
.
Odejmowanie: subss
, subps
.
Mnożenie: mulss
, mulps
.
Dzielenie: divss
, divps
.
Odwrotność (reciprocal, 1/x): rcpss
,
rcpps
. Operacje binarne, np.
rcpps xmm1,xmm2
Pierwiastek kwadratowy: sqrtss
, sqrtps
.
Operacje binarne, np.
sqrtss xmm1,xmm2
Odwrotność pierwiastka kwadratowego: rsqrtss
,
rsqrtps
.
Maksimum: maxss
, maxps
.
Minimum: minss
, minps
.
Przykład: dodawanie 4-elementowych wektorów
;; float *vector_add (float V1[4], float V2[4], float W[4]) global vector_add section .text vector_add: push ebp mov ebp,esp mov eax,[ebp+8] ;Adresy argumentów do rejestrów mov ebx,[ebp+12] movups xmm0,[eax] ;Pobranie argumentów movups xmm1,[ebx] addps xmm0,xmm1 mov eax,[ebp+16] movups [eax],xmm0 ;Wynik pop ebp ret
Instrukcja
movmskps rax,xmm1umieszcza bity znaku dla czterech 32-bitowych liczb zmiennopozycyjnych w podanym rejestrze (na 4 dolnych bitach, reszta wyzerowana).
Średnia arytmetyczna:
pavgb
- dla każdej pary bajtów;
pavgw
- dla każdej pary słów 16-bitowych.
Maksima i minima
pmaxsb
- zwraca maksima dla każdej pary bajtów
traktowanych jako liczby 8-bitowe ze znakiem.
Zmieniając ostatnią literę na w lub d dostajemy to samo dla par liczb
16- i 32-bitowych ze znakiem.
pmaxub
- to samo co powyżej, ale dla liczb bez znaku..
pminsb
- zwraca minima dla każdej pary bajtów (liczby ze
znakiem), inne warianty jak powyżej.
pminub
- to samo co powyżej dla liczb bez znaku.
Egzotyka:
psadbw
zwraca sumę absolutnych różnic par bajtów
(liczby bez znaku). Dla wariantu 64-bitowego (rejestry MMX) suma jest
w dwóch dolnych bajtach argumentu docelowego, reszta wyzerowana.
Dla wariantu 128-bitowego (rejestry XMM) zwraca dwie sumy dla każdej
części 64-bitowej.
andnps
- koniunkcja bitowa z zanegowanym logicznie drugim
argumentem (AND NOT)
andps
- koniunkcja bitowa (AND)
orps
- alternatywa bitowa (OR)
xorps
- alternatywa wyłączająca (XOR)
Porównania nie ustawiają flag (bo mogłoby nie wystarczyć flag), zamiast tego wyniki przekazywane w pierwszym argumencie.
cmpps
, cmpss
- porównuje argumenty, zwraca
same zera lub same jedynki (w pierwszym argumencie).
cmpwwps
- porównuje 4 pary liczb zmiennopozycyjnych
32-bitowych,cmpwwss
- porównuje dwie 32-bitowe liczby
zmiennopozycyjne.Litery ww wybierają sposób porównania (w rzeczywistości jest to podawane dodatkowym bajtem w instrukcji):
eq
- równe
lt
- mniejszy
le
- mniejszy lub równy
ne
- nierówne
nlt
- nie mniejszy
nle
- ani mniejszy, ani równy
ord
- ordered, wolicie nie wiedzieć
unord
- unordered, wolicie nie wiedzieć
Dla pojedynczych liczb zmiennopozycyjnych są porównania ustawiające flagi:
comiss
- porównuje dwie 32-bitowe liczby zmiennopozycyjne
(z najniższej pozycji w sposób klasyczny (zeruje OF, ustawia inne flagi).
ucomiss
- to samo co ucomiss
, ale nie zgłasza
wyjątku dla QNaNs.
Flagi są ustawiane jak dla porównania liczb całkowitych bez znaku, ale flaga parzystości PF jest ustawiana, gdy którymś argumentem jest NaN.
Dla liczb zmiennopozycyjnych:
cvtss2sd
zamienia single na double.
cvtsd2ss
zamienia double na single.
Pojedyncze między liczbą całkowitą a zmiennopozycyjną, argument w zwykłym rejestrze lub w pamięci, wynik w rejestrze XMM (,,na dole''). Pozostałe pozycje nie ulegają zmianie:
cvtsi2ss
- zamienia 32-bitową liczbę całkowitą na liczbę
zmiennopozycyjną pojedynczej precyzji.
cvtsi2sd
- zamienia 32-bitową liczbę całkowitą na liczbę
zmiennopozycyjną podwójnej precyzji.
cvtsi2ssq
- zamienia 64-bitową liczbę całkowitą na liczbę
zmiennopozycyjną pojedynczej precyzji.
cvtsi2sdq
- zamienia 64-bitową liczbę całkowitą na liczbę
zmiennopozycyjną podwójnej precyzji.
Odwrotnie, wynik w zwykłym rejestrze, argument w rejestrze XMM lub w pamięci. Z obcięciem (truncation).
cvtss2si
- zamienia liczbę zmiennopozycyjną pojedynczej
precyzji na 32-bitową liczbę całkowitą.
cvtsd2si
- zamienia liczbę zmiennopozycyjną podwójnej
precyzji na 32-bitową liczbę całkowitą.
cvtss2siq
- zamienia liczbę zmiennopozycyjną pojedynczej
precyzji na 64-bitową liczbę całkowitą.
cvtsd2siq
- zamienia liczbę zmiennopozycyjną podwójnej
precyzji na 64-bitową liczbę całkowitą.
Równoległe:
cvtpi2ps
- zamienia dwie dolne 32-bitowe liczby całkowite na
liczby zmiennopozycyjne. Dwie górne pozycje nie zmieniają się.
cvtps2pi
- odwrotnie, zamienia dwie dolne 32-bitowe liczby
zmiennopozycyjne na liczby całkowite.
Z nasycaniem:
cvttss2si
- zamienia 32-bitową liczbę
zmiennopozycyjną z najniższej pozycji na liczbę całkowitą,
obcinając za duże wyniki do maksymalnej możliwej wartości.
cvttps2pi
- to samo dla dwóch dolnych 32-bitowych liczb
zmiennopozycyjnych.
fxsave
- zachowuje stan jednostek SSE i FPU (a więc także
MMX) w 512-bajtowym obszarze w pamięci.
fxrstor
- odtwarza zachowany stan jednostek SSE i FPU.
stmxcsr
- Zachowuje rejestr mxcsr
.
ldmxcsr
- Ładuje wartość do rejestru mxcsr
.
32-bitowy rejestr mxcsr
zawiera flagi informujące o wynikach
obliczeń i sterujące przebiegiem obliczeń dla instrukcji SSE.
Bity 0-15 są zdefiniowane następująco
Nazwa | Numer bitu | Opis |
---|---|---|
FZ | 15 | Flush To Zero |
R+ | 14 | Round Positive |
R- | 13 | Round Negative |
PM | 12 | Precision Mask |
UM | 11 | Underflow Mask |
OM | 10 | Overflow Mask |
ZM | 9 | Divide By Zero Mask |
DM | 8 | Denormal Mask |
IM | 7 | Invalid Operation Mask |
DAZ | 6 | Denormals Are Zero |
PE | 5 | Precision Flag |
UE | 4 | Underflow Flag |
OE | 3 | Overflow Flag |
ZE | 2 | Divide By Zero Flag |
DE | 1 | Denormal Flag |
IE | 0 | Invalid Operation Flag |
Ustawienie flagi FZ powoduje zwracanie zera w przypadku niedomiaru (underflow). Przyśpiesza obliczenia, ale czasem zmniejsza dokładność.
Flagi R+
i R-
określają kierunek zaokrąglania dla
najniższego bitu. Jeśli obie są ustawione (oznaczane czasem
RZ: Round To Zero), to zaokrąglanie jest do zera. Normalnie obie
są wyzerowane (RN: Round To Nearest) i wtedy zaokrągla się do bliższej
wartości.
Flagi PM
, UM
, MM
, ZM
,
DM
i IM
służą do maskowania odpowiednich wyjątków.
Flaga DAZ
powoduje zamianę powstających Denormals
(małych liczb, które nie mogą być znormalizowane) na zero.
Występuje tylko w niektórych procesorach, przed zmienianiem trzeba upewnić
się, że jest obsługiwany.
Flagi PE
, UE
, ME
, ZE
,
DE
i IE
są ustawiane po wystąpieniu odpowiedniego
wyjątku. Są one ,,lepkie'', tzn. raz ustawione pozostają w tym stanie
i muszą być zerowane ręcznie. Umożliwia to jednokrotne sprawdzanie
na koniec ciągu operacji, ale może powodować zgubienie informacji o tym,
że pewien wyjątek wystąpił kilkakrotnie.
sfence
- Wszystkie zapisy do pamięci zainicjowane przed
tą instrukcją będą wykonane przed zapisami zainicjowanymi po tej
instrukcji.