.intel_syntax noprefix

.data
two: .int 2

.text
.global dot_product

############################################################################
##
## Funkcija:
##
## void dot_product(float *x, float *y, int n, float *r);
##
## izracunava skalarni proizvod vektora x i y dimenzije n, i rezultat smesta
## na adresu r. Argumenti funkcije:
##
## -- float * x -- rdi -- adresa prvog vektora
## -- float * y -- rsi -- adresa drugog vektora
## -- int n -- edx -- dimenzija vektora
## -- float * r -- rcx -- adresa na koju se smesta rezultat.
##
############################################################################
dot_product:
                        ## Prolog funkcije.
    enter   0, 0

                        ## U rdi smestamo adresu prvog, a u rsi
                        ## adresu drugog vektora. U r8 je adresa rezultata.
                        ## Dimenziju smestamo privremeno u r9 zbog test
                        ## instrukcije koja menja rcx
    mov r8, rcx
    mov r9d, edx
        
                        ## Proverava se da li procesor podrzava SSE instrukcije.
    mov     rax, 1
    cpuid
    test    rdx, 0x2000000
    jz not_supported

                        ## Cuva se sadrzaj SSE registara i registara koprocesora.
    mov     rdx, rsp
    and     rsp, 0xfffffffffffffff0
    sub     rsp, 512
    fxsave  [rsp]

                        ## Inicijalizujemo brojac na dimenziju vektora.
    mov ecx, r9d
                        ## Registar xmm0 cemo koristiti za parcijalne sume.
	xorps xmm0, xmm0
	
next_four:
                        ## Dokle god ima bar jos cetiri elementa...
    cmp     ecx, 4
    jb next_one

                        ## ...ucitavamo sledeca cetiri elementa iz oba vektora, mnozimo ih
                        ## i dodajemo na parcijalne sume.
    movups  xmm1, [rsi]
    movups  xmm2, [rdi]
	mulps xmm1, xmm2
	addps xmm0, xmm1
	
        
                        ## Prelazi se na narednu iteraciju.
    add     rsi, 16
	add     rdi, 16  
    sub     ecx, 4
    jmp next_four
        
next_one:
                        ## Dokle god ima jos elemenata...
    jecxz finish


                        ## ...ucitavamo sledece elemente iz oba vektora, mnozimo ih i dodajemo
                        ## na najnizu parcijalnu sumu.
	movss  xmm1, [rsi]
    movss  xmm2, [rdi]
	mulss xmm1, xmm2
	addss xmm0, xmm1
	
                        ## Prelazi se na narednu iteraciju.
    add     rsi, 4
	add     rdi, 4
    dec     ecx
        
    jmp next_one
        
finish:	
                        ## Sabiramo 4 parcijalne sume i dobijamo konacnu sumu u najnizem
                        ## podatku registra xmm0. 
    movhlps xmm1, xmm0
    addps   xmm0, xmm1
    movaps  xmm1, xmm0
    shufps  xmm1, xmm1, 0b01010101
    addss   xmm0, xmm1
    
                        ## Upisujemo podatak na za to predvidjenu lokaciju u memoriji.          
	movss   [r8], xmm0
 
                        ## Vraca se prethodni sadrzaj u registre koprocesora i SSE
                        ## registre.
    fxrstor [rsp]
    mov     rsp, rdx

done:
                        ## Epilog funkcije.
	leave
    ret

not_supported:
                        ## Ako SSE instrukcije nisu podrzane, prekidamo program exit(1)
                        ## pozivom.
    mov rax, 1
    mov rbx, 1
    int 0x80
