# Jan Potocki 2020 .globl filter .type filter, @function .data # Pozwala uzyc rozkazu movdqa zamiast movdqu .align 16 # Macierz k w 4 kopiach - do rejestru xmm zmiescimy dane dla 4 pikseli splotu # 1. wiersz macierzy dopelniony do 32 bitow kernel128_1: .byte -1, -1, 0, 0 .byte -1, -1, 0, 0 .byte -1, -1, 0, 0 .byte -1, -1, 0, 0 # 2. wiersz macierzy dopelniony do 32 bitow kernel128_2: .byte -1, 0, 1, 0 .byte -1, 0, 1, 0 .byte -1, 0, 1, 0 .byte -1, 0, 1, 0 # 3. wiersz macierzy dopelniony do 32 bitow kernel128_3: .byte 0, 1, 1, 0 .byte 0, 1, 1, 0 .byte 0, 1, 1, 0 .byte 0, 1, 1, 0 # Stala na potrzeby normalizacji - wartosc splotu z k miesci sie w przedziale # od 3*(-1*255) do 3*(1*255) czyli <-765,765>, wiec dodanie 765 pozwoli # przesunac ja w przedzial <0,1530>. # Na tym etapie obliczen liczby beda zapisane w 16-bitowej reprezentacji. bias128: .word 765, 765, 765, 765, 765, 765, 765, 765 # Argumenty: # rdi - adres M # rsi - adres W # rdx - rozdzielczosc w poziomie # rcx - rozdzielczosc w pionie .text filter: push %rbp mov %rsp, %rbp push %r15 push %r14 push %r13 push %r12 # r15 - warunek stopu dla petli SIMD, tyle ile zmiesci sie w wierszu mov %rdx, %r15 sub $17, %r15 # r14 - warunek stopu dla petli indeksujacej wiersze mov %rcx, %r14 sub $2, %r14 # r12 - warunek stopu dla petli pomocniczej, dokonczenie wiersza mov %rdx, %r12 sub $2, %r12 # ...w tej implementacji skrajne piksele obrazu wynikowego pomijamy # Wpisanie stalych do rejestrow aby przyspieszyc obliczenia movdqa bias128, %xmm12 movdqa kernel128_1, %xmm13 movdqa kernel128_2, %xmm14 movdqa kernel128_3, %xmm15 # r9 - pomocniczy indeks adresujacy wiersze w pamieci xor %r9, %r9 # r10 - indeks rozdzielczosci pionowej (wierszy) xor %r10, %r10 # Petla indeksujaca wiersze lp1: # r11 - indeks rozdzielczosci poziomej (pikseli w wierszu) xor %r11, %r11 # Podstawowa petla SIMD (16 pikseli na iteracje) lp2: # r8 - adres pikseli w pamieci (suma adresu wiersza i indeksu pikseli) mov %r9, %r8 add %r11, %r8 # Instrukcja pinsrd kopiuje 4 kolejne 8-bitowe piksele do fragmentu xmm, # czyli tyle ile trzeba aby obliczyc splot z jednym wierszem k. Ostatni piksel # zachodzi na pierwszy kolejnego splotu - to nie ma znaczenia, bo kazdy wiersz # macierzy k jest do 32-bitowej postaci dopelniony zerem na ostatnim bajcie. # Instrukcja pmaddubsw oblicza iloczyn 8-bitowych pikseli obrazu i wartosci # kernela, sumuje dwa sasiednie iloczyny i w ich miejsce zapisuje wynik # w 16-bitowej reprezentacji U2. # Gorny wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm0 pinsrd $1, 1(%rdi, %r8, 1), %xmm0 pinsrd $2, 2(%rdi, %r8, 1), %xmm0 pinsrd $3, 3(%rdi, %r8, 1), %xmm0 pmaddubsw %xmm13, %xmm0 pinsrd $0, 4(%rdi, %r8, 1), %xmm1 pinsrd $1, 5(%rdi, %r8, 1), %xmm1 pinsrd $2, 6(%rdi, %r8, 1), %xmm1 pinsrd $3, 7(%rdi, %r8, 1), %xmm1 pmaddubsw %xmm13, %xmm1 pinsrd $0, 8(%rdi, %r8, 1), %xmm6 pinsrd $1, 9(%rdi, %r8, 1), %xmm6 pinsrd $2, 10(%rdi, %r8, 1), %xmm6 pinsrd $3, 11(%rdi, %r8, 1), %xmm6 pmaddubsw %xmm13, %xmm6 pinsrd $0, 12(%rdi, %r8, 1), %xmm7 pinsrd $1, 13(%rdi, %r8, 1), %xmm7 pinsrd $2, 14(%rdi, %r8, 1), %xmm7 pinsrd $3, 15(%rdi, %r8, 1), %xmm7 pmaddubsw %xmm13, %xmm7 add %rdx, %r8 # Przechowanie adresu srodkowego wiersza na pozniej, do zapisu wyniku splotu mov %r8, %r13 # Srodkowy wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm2 pinsrd $1, 1(%rdi, %r8, 1), %xmm2 pinsrd $2, 2(%rdi, %r8, 1), %xmm2 pinsrd $3, 3(%rdi, %r8, 1), %xmm2 pmaddubsw %xmm14, %xmm2 pinsrd $0, 4(%rdi, %r8, 1), %xmm3 pinsrd $1, 5(%rdi, %r8, 1), %xmm3 pinsrd $2, 6(%rdi, %r8, 1), %xmm3 pinsrd $3, 7(%rdi, %r8, 1), %xmm3 pmaddubsw %xmm14, %xmm3 pinsrd $0, 8(%rdi, %r8, 1), %xmm8 pinsrd $1, 9(%rdi, %r8, 1), %xmm8 pinsrd $2, 10(%rdi, %r8, 1), %xmm8 pinsrd $3, 11(%rdi, %r8, 1), %xmm8 pmaddubsw %xmm14, %xmm8 pinsrd $0, 12(%rdi, %r8, 1), %xmm9 pinsrd $1, 13(%rdi, %r8, 1), %xmm9 pinsrd $2, 14(%rdi, %r8, 1), %xmm9 pinsrd $3, 15(%rdi, %r8, 1), %xmm9 pmaddubsw %xmm14, %xmm9 add %rdx, %r8 # Dolny wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm4 pinsrd $1, 1(%rdi, %r8, 1), %xmm4 pinsrd $2, 2(%rdi, %r8, 1), %xmm4 pinsrd $3, 3(%rdi, %r8, 1), %xmm4 pmaddubsw %xmm15, %xmm4 pinsrd $0, 4(%rdi, %r8, 1), %xmm5 pinsrd $1, 5(%rdi, %r8, 1), %xmm5 pinsrd $2, 6(%rdi, %r8, 1), %xmm5 pinsrd $3, 7(%rdi, %r8, 1), %xmm5 pmaddubsw %xmm15, %xmm5 pinsrd $0, 8(%rdi, %r8, 1), %xmm10 pinsrd $1, 9(%rdi, %r8, 1), %xmm10 pinsrd $2, 10(%rdi, %r8, 1), %xmm10 pinsrd $3, 11(%rdi, %r8, 1), %xmm10 pmaddubsw %xmm15, %xmm10 pinsrd $0, 12(%rdi, %r8, 1), %xmm11 pinsrd $1, 13(%rdi, %r8, 1), %xmm11 pinsrd $2, 14(%rdi, %r8, 1), %xmm11 pinsrd $3, 15(%rdi, %r8, 1), %xmm11 pmaddubsw %xmm15, %xmm11 # Suma kolumn splotu paddw %xmm2, %xmm0 paddw %xmm3, %xmm1 paddw %xmm8, %xmm6 paddw %xmm9, %xmm7 paddw %xmm4, %xmm0 paddw %xmm5, %xmm1 paddw %xmm10, %xmm6 paddw %xmm11, %xmm7 # Suma wierszy splotu phaddw %xmm1, %xmm0 phaddw %xmm7, %xmm6 # Normalizacja wyniku splotu - dodanie zdefiniowanego obciazenia i skalowanie # z powrotem do 8-bitowej reprezentacji. # Dokladny wynik pozwoliloby uzyskac dzielenie przez 6 (1530/6=255), # przesuniecie o 3 bity w prawo jest wydajniejsze, a wciaz pozwala uzyskac # rozsadna dokladnosc (1530>>3=191) paddw %xmm12, %xmm0 paddw %xmm12, %xmm6 psrlw $3, %xmm0 psrlw $3, %xmm6 # Spakowanie 8-bitowych wynikow do jednego rejestru i zapis do pamieci packuswb %xmm6, %xmm0 movdqu %xmm0, 1(%rsi, %r13, 1) # Sprawdzenie, czy nastepna iteracja petli SIMD zmiesci sie w wierszu add $16, %r11 cmp %r15, %r11 jl lp2 # Sprawdzenie, czy wiersz zostal przeliczony w calosci... cmp %r12, %r11 je next_row # ...jesli nie - na dobieg pomocnicza petla (1 piksel na iteracje) lp2a: mov %r9, %r8 add %r11, %r8 # Gorny wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm0 pmaddubsw %xmm13, %xmm0 add %rdx, %r8 mov %r8, %r13 # Srodkowy wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm2 pmaddubsw %xmm14, %xmm2 add %rdx, %r8 # Dolny wiersz macierzy k pinsrd $0, 0(%rdi, %r8, 1), %xmm4 pmaddubsw %xmm15, %xmm4 # Suma i normalizacja splotu paddw %xmm2, %xmm0 paddw %xmm4, %xmm0 phaddw %xmm0, %xmm0 paddw %xmm12, %xmm0 psrlw $3, %xmm0 # Spakowanie 8-bitowego wyniku w najmlodszym fragmencie rejestru i zapis packuswb %xmm0, %xmm0 pextrb $0, %xmm0, 1(%rsi, %r13, 1) # Sprawdzenie, czy tym razem wiersz jest juz przeliczony w calosci inc %r11 cmp %r12, %r11 jl lp2a next_row: # Przesuniecie adresu wiersza w dol add %rdx, %r9 # Sprawdzenie, czy caly obraz jest przeliczony inc %r10 cmp %r14, %r10 jl lp1 pop %r12 pop %r13 pop %r14 pop %r15 pop %rbp ret