+45% RC4 performance boost on Intel EM64T core. Unrolled loop providing
[openssl.git] / crypto / rc4 / asm / rc4-amd64.pl
index 9ca0125..2d3dedd 100755 (executable)
 # Presumably it has everything to do with AMD cache architecture and
 # RAW or whatever penalties. Once again! The module *requires* config
 # line *without* RC4_CHAR! As for coding "secret," I bet on partial
-# register arithmetics. For example instead 'inc %r8; and $255,%r8'
+# register arithmetics. For example instead of 'inc %r8; and $255,%r8'
 # I simply 'inc %r8b'. Even though optimization manual discourages
 # to operate on partial registers, it turned out to be the best bet.
 # At least for AMD... How IA32E would perform remains to be seen...
 
+# As was shown by Marc Bevand reordering of couple of load operations
+# results in even higher performance gain of 3.3x:-) At least on
+# Opteron... For reference, 1x in this case is RC4_CHAR C-code
+# compiled with gcc 3.3.2, which performs at ~54MBps per 1GHz clock.
+# Latter means that if you want to *estimate* what to expect from
+# *your* CPU, then multiply 54 by 3.3 and clock frequency in GHz.
+
+# Intel P4 EM64T core was found to run the AMD64 code really slow...
+# The only way to achieve comparable performance on P4 is to keep
+# RC4_CHAR. Kind of ironic, huh? As it's apparently impossible to
+# compose blended code, which would perform even within 30% marginal
+# on either AMD and Intel platforms, I implement both cases. See
+# rc4_skey.c for further details... This applies to 0.9.8 and later.
+# In 0.9.7 context RC4_CHAR codepath is never engaged and ~70 bytes
+# of code remain redundant.
+
 $output=shift;
 
 $win64a=1 if ($output =~ /win64a.[s|asm]/);
@@ -65,10 +81,10 @@ RC4:        or      $len,$len
 .Lentry:
 ___
 $code=<<___ if (defined($win64a));
-TEXT   SEGMENT
+_TEXT  SEGMENT
 PUBLIC RC4
 ALIGN  16
-RC4    PROC NEAR
+RC4    PROC
        or      $len,$len
        jne     .Lentry
        repret
@@ -83,51 +99,50 @@ $code.=<<___;
        add     \$8,$dat
        movl    `&PTR("DWORD:-8[$dat]")`,$XX#d
        movl    `&PTR("DWORD:-4[$dat]")`,$YY#d
+       cmpl    \$-1,`&PTR("DWORD:256[$dat]")`
+       je      .LRC4_CHAR
        test    \$-8,$len
        jz      .Lloop1
 .align 16
 .Lloop8:
-       movq    `&PTR("QWORD:[$inp]")`,%rax
-
        inc     $XX#b
        movl    `&PTR("DWORD:[$dat+$XX*4]")`,$TX#d
        add     $TX#b,$YY#b
        movl    `&PTR("DWORD:[$dat+$YY*4]")`,$TY#d
        movl    $TX#d,`&PTR("DWORD:[$dat+$YY*4]")`
        movl    $TY#d,`&PTR("DWORD:[$dat+$XX*4]")`
-       add     $TY#b,$TX#b
+       add     $TX#b,$TY#b
        inc     $XX#b
-       movl    `&PTR("DWORD:[$dat+$TX*4]")`,$TY#d
-       xor     $TY,%rax
+       movl    `&PTR("DWORD:[$dat+$XX*4]")`,$TX#d
+       movb    `&PTR("BYTE:[$dat+$TY*4]")`,%al
 ___
 for ($i=1;$i<=6;$i++) {
 $code.=<<___;
-       movl    `&PTR("DWORD:[$dat+$XX*4]")`,$TX#d
        add     $TX#b,$YY#b
+       ror     \$8,%rax
        movl    `&PTR("DWORD:[$dat+$YY*4]")`,$TY#d
        movl    $TX#d,`&PTR("DWORD:[$dat+$YY*4]")`
        movl    $TY#d,`&PTR("DWORD:[$dat+$XX*4]")`
-       add     $TY#b,$TX#b
-       movl    `&PTR("DWORD:[$dat+$TX*4]")`,$TY#d
-       shl     \$`8*$i`,$TY
+       add     $TX#b,$TY#b
        inc     $XX#b
-       xor     $TY,%rax
+       movl    `&PTR("DWORD:[$dat+$XX*4]")`,$TX#d
+       movb    `&PTR("BYTE:[$dat+$TY*4]")`,%al
 ___
 }
 $code.=<<___;
-       movl    `&PTR("DWORD:[$dat+$XX*4]")`,$TX#d
        add     $TX#b,$YY#b
+       ror     \$8,%rax
        movl    `&PTR("DWORD:[$dat+$YY*4]")`,$TY#d
        movl    $TX#d,`&PTR("DWORD:[$dat+$YY*4]")`
        movl    $TY#d,`&PTR("DWORD:[$dat+$XX*4]")`
        sub     \$8,$len
        add     $TY#b,$TX#b
-       add     \$8,$out
-       movl    `&PTR("DWORD:[$dat+$TX*4]")`,$TY#d
-       shl     \$56,$TY
+       movb    `&PTR("BYTE:[$dat+$TX*4]")`,%al
+       ror     \$8,%rax
        add     \$8,$inp
-       xor     $TY,%rax
+       add     \$8,$out
 
+       xor     `&PTR("QWORD:-8[$inp]")`,%rax
        mov     %rax,`&PTR("QWORD:-8[$out]")`
 
        test    \$-8,$len
@@ -163,10 +178,28 @@ $code.=<<___;
        dec     $len
        jnz     .Lloop1
        jmp     .Lexit
+
+.align 16
+.LRC4_CHAR:
+       add     \$1,$XX#b
+       movzb   `&PTR("BYTE:[$dat+$XX]")`,$TX#d
+       add     $TX#b,$YY#b
+       movzb   `&PTR("BYTE:[$dat+$YY]")`,$TY#d
+       movb    $TX#b,`&PTR("BYTE:[$dat+$YY]")`
+       movb    $TY#b,`&PTR("BYTE:[$dat+$XX]")`
+       add     $TX#b,$TY#b
+       movzb   `&PTR("BYTE:[$dat+$TY]")`,$TY#d
+       xorb    `&PTR("BYTE:[$inp]")`,$TY#b
+       movb    $TY#b,`&PTR("BYTE:[$out]")`
+       lea     1($inp),$inp
+       lea     1($out),$out
+       sub     \$1,$len
+       jnz     .LRC4_CHAR
+       jmp     .Lexit
 ___
 $code.=<<___ if (defined($win64a));
 RC4    ENDP
-TEXT   ENDS
+_TEXT  ENDS
 END
 ___
 $code.=<<___ if (!defined($win64a));
@@ -185,6 +218,8 @@ if (defined($win64a)) {
     $code =~ s/mov[bwlq]/mov/gm;
     $code =~ s/movzb/movzx/gm;
     $code =~ s/repret/DB\t0F3h,0C3h/gm;
+    $code =~ s/cmpl/cmp/gm;
+    $code =~ s/xorb/xor/gm;
 } else {
     $code =~ s/([QD]*WORD|BYTE)://gm;
     $code =~ s/repret/.byte\t0xF3,0xC3/gm;