#define N_SUMS 32

#define FNV_PRIME 16777619

static uint32 multiplyVectors[4] =
{   
	FNV_PRIME, FNV_PRIME, FNV_PRIME, FNV_PRIME   
};

static uint32 checksumOffsets[32] = {
	0x5B1F36E9, 0xB8525960, 0x02AB50AA, 0x1DE66D2A,
	0x79FF467A, 0x9BB9F8A3, 0x217E7CD2, 0x83E13D2C,
	0xF8D4474F, 0xE39EB970, 0x42C6AE16, 0x993216FA,
	0x7B093B5D, 0x98DAFF3C, 0xF718902A, 0x0B1C9CDB,
	0xE58F764B, 0x187636BC, 0x5D7B3BB1, 0xE73DE7DE,
	0x92BEC979, 0xCCA6C0B2, 0x304A0979, 0x85AA43D4,
	0x783125BB, 0x6CA8EAA2, 0xE407EAC6, 0x4B5CFC3E,
	0x9FBF8C76, 0x15CA20BE, 0xF2CA9FD3, 0x959BD756
};


#ifndef DONT_USE_SRL
__attribute__ ((noinline)) static uint16
checksum_fast(char *page, uint32 blkno)
{
        uint32 result;
        uint16 checksum;

        asm __volatile__ (
"mov    $0x80, %%rdx\n" // vector length
"xor    %%rcx, %%rcx\n" // offset

"movdqa (%4), %%xmm0\n"
"movdqa 0x10(%4), %%xmm1\n"
"movdqa 0x20(%4), %%xmm2\n"
"movdqa 0x30(%4), %%xmm3\n"
"movdqa 0x40(%4), %%xmm4\n"
"movdqa 0x50(%4), %%xmm5\n"
"movdqa 0x60(%4), %%xmm6\n"
"movdqa 0x70(%4), %%xmm7\n"

"movdqa (%2), %%xmm9\n"

/* first vector of the first iteration needs to zero out the checksum */
"movdqu (%1,%%rcx,1), %%xmm8\n"
"pinsrw $0x4, %%ecx, %%xmm8\n" // zero out checksum
"jmp 2f\n"

// main loop, accumulate hash codes in parallel

"1:\n"
".align 16\n"
"movdqu  (%1,%%rcx,1), %%xmm8\n"
"2:\n"
"pxor    %%xmm8, %%xmm0\n"
"movdqa  %%xmm0, %%xmm10\n"
"pmulld  %%xmm9, %%xmm0\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm0\n"

"movdqu  0x10(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm1\n"
"movdqa  %%xmm1, %%xmm10\n"
"pmulld  %%xmm9, %%xmm1\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm1\n"

"movdqu  0x20(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm2\n"
"movdqa  %%xmm2, %%xmm10\n"
"pmulld  %%xmm9, %%xmm2\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm2\n"

"movdqu  0x30(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm3\n"
"movdqa  %%xmm3, %%xmm10\n"
"pmulld  %%xmm9, %%xmm3\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm3\n"

"movdqu  0x40(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm4\n"
"movdqa  %%xmm4, %%xmm10\n"
"pmulld  %%xmm9, %%xmm4\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm4\n"

"movdqu  0x50(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm5\n"
"movdqa  %%xmm5, %%xmm10\n"
"pmulld  %%xmm9, %%xmm5\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm5\n"

"movdqu  0x60(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm6\n"
"movdqa  %%xmm6, %%xmm10\n"
"pmulld  %%xmm9, %%xmm6\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm6\n"

"movdqu  0x70(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm7\n"
"movdqa  %%xmm7, %%xmm10\n"
"pmulld  %%xmm9, %%xmm7\n"
"psrld   $0x3,   %%xmm10\n"
"pxor    %%xmm10, %%xmm7\n"

"add     %%rdx, %%rcx\n"
"cmp %3, %%ecx\n"
"jnz 1b\n"

/* xor fold */
"pxor    %%xmm0, %%xmm1\n"
"pxor    %%xmm2, %%xmm3\n"
"pxor    %%xmm4, %%xmm5\n"
"pxor    %%xmm6, %%xmm7\n"
"pxor    %%xmm1, %%xmm3\n"
"pxor    %%xmm5, %%xmm7\n"
"pxor    %%xmm3, %%xmm7\n"
"movdqa  %%xmm7, %%xmm0\n"
"psrldq  $0x8, %%xmm7\n"
"pxor    %%xmm7, %%xmm0\n"
"movdqa  %%xmm0, %%xmm1\n"
"psrldq  $0x4, %%xmm1\n"
"pxor    %%xmm1, %%xmm0\n"

"movd %%xmm0, %0\n"

: "=r"(result)
: "r"(page), "r"(multiplyVectors), "r"(PAGE_SIZE), "r"(checksumOffsets)
: "rcx","rdx","xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7","xmm8", "xmm9","xmm10","xmm11"
);
        result ^= blkno;
        checksum = (result % 65521) + 1;
	return checksum;
}

#else

__attribute__ ((noinline)) static uint16
checksum_fast(char *page, uint32 blkno)
{
        uint32 result;
        uint16 checksum;

        asm __volatile__ (
"mov    $0x80, %%rdx\n" // vector length
"xor    %%rcx, %%rcx\n" // offset

"movdqa (%4), %%xmm0\n"
"movdqa 0x10(%4), %%xmm1\n"
"movdqa 0x20(%4), %%xmm2\n"
"movdqa 0x30(%4), %%xmm3\n"
"movdqa 0x40(%4), %%xmm4\n"
"movdqa 0x50(%4), %%xmm5\n"
"movdqa 0x60(%4), %%xmm6\n"
"movdqa 0x70(%4), %%xmm7\n"

"movdqa (%2), %%xmm9\n"

/* first vector of the first iteration needs to zero out the checksum */
"movdqu (%1,%%rcx,1), %%xmm8\n"
"pinsrw $0x4, %%ecx, %%xmm8\n" // zero out checksum
"jmp 2f\n"

// main loop, accumulate hash codes in parallel
"1:\n"
".align 16\n"
"movdqu  (%1,%%rcx,1), %%xmm8\n"
"2:\n"
"pxor    %%xmm8, %%xmm0\n"
"pmulld  %%xmm9, %%xmm0\n"

"movdqu  0x10(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm1\n"
"pmulld  %%xmm9, %%xmm1\n"

"movdqu  0x20(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm2\n"
"pmulld  %%xmm9, %%xmm2\n"

"movdqu  0x30(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm3\n"
"pmulld  %%xmm9, %%xmm3\n"

"movdqu  0x40(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm4\n"
"pmulld  %%xmm9, %%xmm4\n"

"movdqu  0x50(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm5\n"
"pmulld  %%xmm9, %%xmm5\n"

"movdqu  0x60(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm6\n"
"pmulld  %%xmm9, %%xmm6\n"

"movdqu  0x70(%1,%%rcx,1), %%xmm8\n"
"pxor    %%xmm8, %%xmm7\n"
"pmulld  %%xmm9, %%xmm7\n"

"add     %%rdx, %%rcx\n"
"cmp %3, %%ecx\n"
"jnz 1b\n"

/* xor fold */
"pxor    %%xmm0, %%xmm1\n"
"pxor    %%xmm2, %%xmm3\n"
"pxor    %%xmm4, %%xmm5\n"
"pxor    %%xmm6, %%xmm7\n"
"pxor    %%xmm1, %%xmm3\n"
"pxor    %%xmm5, %%xmm7\n"
"pxor    %%xmm3, %%xmm7\n"
"movdqa  %%xmm7, %%xmm0\n"
"psrldq  $0x8, %%xmm7\n"
"pxor    %%xmm7, %%xmm0\n"
"movdqa  %%xmm0, %%xmm1\n"
"psrldq  $0x4, %%xmm1\n"
"pxor    %%xmm1, %%xmm0\n"

"movd %%xmm0, %0\n"

: "=r"(result)
: "r"(page), "r"(multiplyVectors), "r"(PAGE_SIZE), "r"(checksumOffsets)
: "rcx","rdx","xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7","xmm8", "xmm9","xmm10","xmm11"
);
        result ^= blkno;
        checksum = (result % 65521) + 1;
	return checksum;
}
#endif

