From 6f57311da0808311240f1563dd3dab67c524367d Mon Sep 17 00:00:00 2001 From: Andy Polyakov Date: Sun, 11 Nov 2007 13:56:47 +0000 Subject: [PATCH] Add SHA x86_64 assembler [from HEAD]. --- Configure | 2 +- TABLE | 8 +- crypto/perlasm/x86_64-xlate.pl | 59 +++++- crypto/sha/Makefile | 5 + crypto/sha/asm/sha1-x86_64.pl | 242 ++++++++++++++++++++++ crypto/sha/asm/sha512-x86_64.pl | 344 ++++++++++++++++++++++++++++++++ 6 files changed, 645 insertions(+), 15 deletions(-) create mode 100755 crypto/sha/asm/sha1-x86_64.pl create mode 100755 crypto/sha/asm/sha512-x86_64.pl diff --git a/Configure b/Configure index c4fb17ef5a..2b3bf5ef30 100755 --- a/Configure +++ b/Configure @@ -118,7 +118,7 @@ my $x86_elf_asm="x86cpuid-elf.o:bn86-elf.o co86-elf.o:dx86-elf.o yx86-elf.o:ax86 my $x86_coff_asm="x86cpuid-cof.o:bn86-cof.o co86-cof.o:dx86-cof.o yx86-cof.o:ax86-cof.o:bx86-cof.o:mx86-cof.o:sx86-cof.o s512sse2-cof.o:cx86-cof.o:rx86-cof.o:rm86-cof.o:r586-cof.o"; my $x86_out_asm="x86cpuid-out.o:bn86-out.o co86-out.o:dx86-out.o yx86-out.o:ax86-out.o:bx86-out.o:mx86-out.o:sx86-out.o s512sse2-out.o:cx86-out.o:rx86-out.o:rm86-out.o:r586-out.o"; -my $x86_64_asm="x86_64cpuid.o:x86_64-gcc.o::::md5-x86_64.o:::rc4-x86_64.o::"; +my $x86_64_asm="x86_64cpuid.o:x86_64-gcc.o::::md5-x86_64.o:sha1-x86_64.o sha256-x86_64.o sha512-x86_64.o::rc4-x86_64.o::"; my $ia64_asm=":bn-ia64.o::aes_core.o aes_cbc.o aes-ia64.o:::sha1-ia64.o sha256-ia64.o sha512-ia64.o::rc4-ia64.o::"; my $no_asm="::::::::::"; diff --git a/TABLE b/TABLE index 689baf9205..62f05488b4 100644 --- a/TABLE +++ b/TABLE @@ -257,7 +257,7 @@ $des_obj = $aes_obj = $bf_obj = $md5_obj = md5-x86_64.o -$sha1_obj = +$sha1_obj = sha1-x86_64.o sha256-x86_64.o sha512-x86_64.o $cast_obj = $rc4_obj = rc4-x86_64.o $rmd160_obj = @@ -2957,7 +2957,7 @@ $des_obj = $aes_obj = $bf_obj = $md5_obj = md5-x86_64.o -$sha1_obj = +$sha1_obj = sha1-x86_64.o sha256-x86_64.o sha512-x86_64.o $cast_obj = $rc4_obj = rc4-x86_64.o $rmd160_obj = @@ -3740,7 +3740,7 @@ $des_obj = $aes_obj = $bf_obj = $md5_obj = md5-x86_64.o -$sha1_obj = +$sha1_obj = sha1-x86_64.o sha256-x86_64.o sha512-x86_64.o $cast_obj = $rc4_obj = rc4-x86_64.o $rmd160_obj = @@ -3767,7 +3767,7 @@ $des_obj = $aes_obj = $bf_obj = $md5_obj = md5-x86_64.o -$sha1_obj = +$sha1_obj = sha1-x86_64.o sha256-x86_64.o sha512-x86_64.o $cast_obj = $rc4_obj = rc4-x86_64.o $rmd160_obj = diff --git a/crypto/perlasm/x86_64-xlate.pl b/crypto/perlasm/x86_64-xlate.pl index ef1a4ce656..6cca5dc005 100755 --- a/crypto/perlasm/x86_64-xlate.pl +++ b/crypto/perlasm/x86_64-xlate.pl @@ -57,7 +57,13 @@ # lea .Label-.Lpic_point(%rcx),%rbp my $output = shift; -open STDOUT,">$output" || die "can't open $output: $!"; + +{ my ($stddev,$stdino,@junk)=stat(STDOUT); + my ($outdev,$outino,@junk)=stat($output); + + open STDOUT,">$output" || die "can't open $output: $!" + if ($stddev!=$outdev || $stdino!=$outino); +} my $masm=1 if ($output =~ /\.asm/); @@ -70,7 +76,7 @@ my $current_function; local *line = shift; undef $ret; - if ($line =~ /^([a-z]+)/i) { + if ($line =~ /^([a-z][a-z0-9]*)/i) { $self->{op} = $1; $ret = $self; $line = substr($line,@+[0]); $line =~ s/^\s+//; @@ -79,7 +85,9 @@ my $current_function; if ($self->{op} =~ /(movz)b.*/) { # movz is pain... $self->{op} = $1; $self->{sz} = "b"; - } elsif ($self->{op} =~ /([a-z]{3,})([qlwb])/) { + } elsif ($self->{op} =~ /call/) { + $self->{sz} = "" + } elsif ($self->{op} =~ /([a-z]{3,})([qlwb])\b/) { $self->{op} = $1; $self->{sz} = $2; } @@ -95,8 +103,10 @@ my $current_function; sub out { my $self = shift; if (!$masm) { - if ($self->{op} eq "movz") { # movz in pain... + if ($self->{op} eq "movz") { # movz is pain... sprintf "%s%s%s",$self->{op},$self->{sz},shift; + } elsif ($self->{op} =~ /^set/) { + "$self->{op}"; } elsif ($self->{op} eq "ret") { ".byte 0xf3,0xc3"; } else { @@ -133,6 +143,10 @@ my $current_function; my $self = shift; if (!$masm) { + # Solaris /usr/ccs/bin/as can't handle multiplications + # in $self->{value} + $self->{value} =~ s/(?{value} =~ s/([0-9]+\s*[\*\/\%]\s*[0-9]+)/eval($1)/eg; sprintf "\$%s",$self->{value}; } else { $self->{value} =~ s/0x([0-9a-f]+)/0$1h/ig; @@ -163,14 +177,16 @@ my $current_function; my $self = shift; my $sz = shift; + # Silently convert all EAs to 64-bit. This is required for + # elder GNU assembler and results in more compact code, + # *but* most importantly AES module depends on this feature! + $self->{index} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/; + $self->{base} =~ s/^[er](.?[0-9xpi])[d]?$/r\1/; + if (!$masm) { - # elder GNU assembler insists on 64-bit EAs:-( - # on pros side, this results in more compact code:-) - $self->{index} =~ s/^[er](.?[0-9xp])[d]?$/r\1/; - $self->{base} =~ s/^[er](.?[0-9xp])[d]?$/r\1/; # Solaris /usr/ccs/bin/as can't handle multiplications # in $self->{label} - $self->{label} =~ s/(?{label} =~ s/(?{label} =~ s/([0-9]+\s*[\*\/\%]\s*[0-9]+)/eval($1)/eg; if (defined($self->{index})) { @@ -192,6 +208,8 @@ my $current_function; $self->{label}, $self->{index},$self->{scale}, $self->{base}; + } elsif ($self->{base} eq "rip") { + sprintf "%s PTR %s",$szmap{$sz},$self->{label}; } else { sprintf "%s PTR %s[%s]",$szmap{$sz}, $self->{label},$self->{base}; @@ -317,6 +335,10 @@ my $current_function; $line =~ s/\@function.*/\@function/; if ($line =~ /\.picmeup\s+(%r[\w]+)/i) { $self->{value} = sprintf "\t.long\t0x%x,0x90000000",$opcode{$1}; + } elsif ($line =~ /\.asciz\s+"(.*)"$/) { + $self->{value} = ".byte\t".join(",",unpack("C*",$1),0); + } elsif ($line =~ /\.extern/) { + $self->{value} = ""; # swallow extern } else { $self->{value} = $line; } @@ -338,6 +360,7 @@ my $current_function; $self->{value} = $v; last; }; + /\.extern/ && do { $self->{value} = "EXTRN\t".$line.":BYTE"; last; }; /\.globl/ && do { $self->{value} = "PUBLIC\t".$line; last; }; /\.type/ && do { ($sym,$type,$narg) = split(',',$line); if ($type eq "\@function") { @@ -372,6 +395,19 @@ my $current_function; /\.picmeup/ && do { $self->{value} = sprintf"\tDD\t 0%Xh,090000000h",$opcode{$line}; last; }; + /\.asciz/ && do { if ($line =~ /^"(.*)"$/) { + my @str=unpack("C*",$1); + push @str,0; + while ($#str>15) { + $self->{value}.="DB\t" + .join(",",@str[0..15])."\n"; + foreach (0..15) { shift @str; } + } + $self->{value}.="DB\t" + .join(",",@str) if (@str); + } + last; + }; } $line = ""; } @@ -480,7 +516,10 @@ close STDOUT; # arguments passed to callee, *but* not less than 4! This means that # upon function entry point 5th argument resides at 40(%rsp), as well # as that 32 bytes from 8(%rsp) can always be used as temporal -# storage [without allocating a frame]. +# storage [without allocating a frame]. One can actually argue that +# one can assume a "red zone" above stack pointer under Win64 as well. +# Point is that at apparently no occasion Windows kernel would alter +# the area above user stack pointer in true asynchronous manner... # # All the above means that if assembler programmer adheres to Unix # register and stack layout, but disregards the "red zone" existense, diff --git a/crypto/sha/Makefile b/crypto/sha/Makefile index 42a8c5b443..ac64fb61d3 100644 --- a/crypto/sha/Makefile +++ b/crypto/sha/Makefile @@ -65,6 +65,11 @@ sha256-ia64.s: asm/sha512-ia64.pl sha512-ia64.s: asm/sha512-ia64.pl (cd asm; $(PERL) sha512-ia64.pl ../$@ $(CFLAGS)) +# Solaris make has to be explicitly told +sha1-x86_64.s: asm/sha1-x86_64.pl; $(PERL) asm/sha1-x86_64.pl $@ +sha256-x86_64.s:asm/sha512-x86_64.pl; $(PERL) asm/sha512-x86_64.pl $@ +sha512-x86_64.s:asm/sha512-x86_64.pl; $(PERL) asm/sha512-x86_64.pl $@ + files: $(PERL) $(TOP)/util/files.pl Makefile >> $(TOP)/MINFO diff --git a/crypto/sha/asm/sha1-x86_64.pl b/crypto/sha/asm/sha1-x86_64.pl new file mode 100755 index 0000000000..f7ed67a726 --- /dev/null +++ b/crypto/sha/asm/sha1-x86_64.pl @@ -0,0 +1,242 @@ +#!/usr/bin/env perl +# +# ==================================================================== +# Written by Andy Polyakov for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# ==================================================================== +# +# sha1_block procedure for x86_64. +# +# It was brought to my attention that on EM64T compiler-generated code +# was far behind 32-bit assembler implementation. This is unlike on +# Opteron where compiler-generated code was only 15% behind 32-bit +# assembler, which originally made it hard to motivate the effort. +# There was suggestion to mechanically translate 32-bit code, but I +# dismissed it, reasoning that x86_64 offers enough register bank +# capacity to fully utilize SHA-1 parallelism. Therefore this fresh +# implementation:-) However! While 64-bit code does performs better +# on Opteron, I failed to beat 32-bit assembler on EM64T core. Well, +# x86_64 does offer larger *addressable* bank, but out-of-order core +# reaches for even more registers through dynamic aliasing, and EM64T +# core must have managed to run-time optimize even 32-bit code just as +# good as 64-bit one. Performance improvement is summarized in the +# following table: +# +# gcc 3.4 32-bit asm cycles/byte +# Opteron +45% +20% 6.8 +# Xeon P4 +65% +0% 9.9 +# Core2 +60% +10% 7.0 + +$output=shift; + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or +( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or +die "can't locate x86_64-xlate.pl"; + +open STDOUT,"| $^X $xlate $output"; + +$ctx="%rdi"; # 1st arg +$inp="%rsi"; # 2nd arg +$num="%rdx"; # 3rd arg + +# reassign arguments in order to produce more compact code +$ctx="%r8"; +$inp="%r9"; +$num="%r10"; + +$xi="%eax"; +$t0="%ebx"; +$t1="%ecx"; +$A="%edx"; +$B="%esi"; +$C="%edi"; +$D="%ebp"; +$E="%r11d"; +$T="%r12d"; + +@V=($A,$B,$C,$D,$E,$T); + +sub PROLOGUE { +my $func=shift; +$code.=<<___; +.globl $func +.type $func,\@function,3 +.align 16 +$func: + push %rbx + push %rbp + push %r12 + mov %rsp,%rax + mov %rdi,$ctx # reassigned argument + sub \$`8+16*4`,%rsp + mov %rsi,$inp # reassigned argument + and \$-64,%rsp + mov %rdx,$num # reassigned argument + mov %rax,`16*4`(%rsp) + + mov 0($ctx),$A + mov 4($ctx),$B + mov 8($ctx),$C + mov 12($ctx),$D + mov 16($ctx),$E +___ +} + +sub EPILOGUE { +my $func=shift; +$code.=<<___; + mov `16*4`(%rsp),%rsp + pop %r12 + pop %rbp + pop %rbx + ret +.size $func,.-$func +___ +} + +sub BODY_00_19 { +my ($i,$a,$b,$c,$d,$e,$f,$host)=@_; +my $j=$i+1; +$code.=<<___ if ($i==0); + mov `4*$i`($inp),$xi + `"bswap $xi" if(!defined($host))` + mov $xi,`4*$i`(%rsp) +___ +$code.=<<___ if ($i<15); + lea 0x5a827999($xi,$e),$f + mov $c,$t0 + mov `4*$j`($inp),$xi + mov $a,$e + xor $d,$t0 + `"bswap $xi" if(!defined($host))` + rol \$5,$e + and $b,$t0 + mov $xi,`4*$j`(%rsp) + add $e,$f + xor $d,$t0 + rol \$30,$b + add $t0,$f +___ +$code.=<<___ if ($i>=15); + lea 0x5a827999($xi,$e),$f + mov `4*($j%16)`(%rsp),$xi + mov $c,$t0 + mov $a,$e + xor `4*(($j+2)%16)`(%rsp),$xi + xor $d,$t0 + rol \$5,$e + xor `4*(($j+8)%16)`(%rsp),$xi + and $b,$t0 + add $e,$f + xor `4*(($j+13)%16)`(%rsp),$xi + xor $d,$t0 + rol \$30,$b + add $t0,$f + rol \$1,$xi + mov $xi,`4*($j%16)`(%rsp) +___ +} + +sub BODY_20_39 { +my ($i,$a,$b,$c,$d,$e,$f)=@_; +my $j=$i+1; +my $K=($i<40)?0x6ed9eba1:0xca62c1d6; +$code.=<<___ if ($i<79); + lea $K($xi,$e),$f + mov `4*($j%16)`(%rsp),$xi + mov $c,$t0 + mov $a,$e + xor `4*(($j+2)%16)`(%rsp),$xi + xor $b,$t0 + rol \$5,$e + xor `4*(($j+8)%16)`(%rsp),$xi + xor $d,$t0 + add $e,$f + xor `4*(($j+13)%16)`(%rsp),$xi + rol \$30,$b + add $t0,$f + rol \$1,$xi +___ +$code.=<<___ if ($i<76); + mov $xi,`4*($j%16)`(%rsp) +___ +$code.=<<___ if ($i==79); + lea $K($xi,$e),$f + mov $c,$t0 + mov $a,$e + xor $b,$t0 + rol \$5,$e + xor $d,$t0 + add $e,$f + rol \$30,$b + add $t0,$f +___ +} + +sub BODY_40_59 { +my ($i,$a,$b,$c,$d,$e,$f)=@_; +my $j=$i+1; +$code.=<<___; + lea 0x8f1bbcdc($xi,$e),$f + mov `4*($j%16)`(%rsp),$xi + mov $b,$t0 + mov $b,$t1 + xor `4*(($j+2)%16)`(%rsp),$xi + mov $a,$e + and $c,$t0 + xor `4*(($j+8)%16)`(%rsp),$xi + or $c,$t1 + rol \$5,$e + xor `4*(($j+13)%16)`(%rsp),$xi + and $d,$t1 + add $e,$f + rol \$1,$xi + or $t1,$t0 + rol \$30,$b + mov $xi,`4*($j%16)`(%rsp) + add $t0,$f +___ +} + +$code=".text\n"; + +&PROLOGUE("sha1_block_data_order"); +$code.=".align 4\n.Lloop:\n"; +for($i=0;$i<20;$i++) { &BODY_00_19($i,@V); unshift(@V,pop(@V)); } +for(;$i<40;$i++) { &BODY_20_39($i,@V); unshift(@V,pop(@V)); } +for(;$i<60;$i++) { &BODY_40_59($i,@V); unshift(@V,pop(@V)); } +for(;$i<80;$i++) { &BODY_20_39($i,@V); unshift(@V,pop(@V)); } +$code.=<<___; + add 0($ctx),$E + add 4($ctx),$T + add 8($ctx),$A + add 12($ctx),$B + add 16($ctx),$C + mov $E,0($ctx) + mov $T,4($ctx) + mov $A,8($ctx) + mov $B,12($ctx) + mov $C,16($ctx) + + xchg $E,$A # mov $E,$A + xchg $T,$B # mov $T,$B + xchg $E,$C # mov $A,$C + xchg $T,$D # mov $B,$D + # mov $C,$E + lea `16*4`($inp),$inp + sub \$1,$num + jnz .Lloop +___ +&EPILOGUE("sha1_block_data_order"); +$code.=<<___; +.asciz "SHA1 block transform for x86_64, CRYPTOGAMS by " +___ + +#################################################################### + +$code =~ s/\`([^\`]*)\`/eval $1/gem; +print $code; +close STDOUT; diff --git a/crypto/sha/asm/sha512-x86_64.pl b/crypto/sha/asm/sha512-x86_64.pl new file mode 100755 index 0000000000..b6252d31ec --- /dev/null +++ b/crypto/sha/asm/sha512-x86_64.pl @@ -0,0 +1,344 @@ +#!/usr/bin/env perl +# +# ==================================================================== +# Written by Andy Polyakov for the OpenSSL +# project. Rights for redistribution and usage in source and binary +# forms are granted according to the OpenSSL license. +# ==================================================================== +# +# sha256/512_block procedure for x86_64. +# +# 40% improvement over compiler-generated code on Opteron. On EM64T +# sha256 was observed to run >80% faster and sha512 - >40%. No magical +# tricks, just straight implementation... I really wonder why gcc +# [being armed with inline assembler] fails to generate as fast code. +# The only thing which is cool about this module is that it's very +# same instruction sequence used for both SHA-256 and SHA-512. In +# former case the instructions operate on 32-bit operands, while in +# latter - on 64-bit ones. All I had to do is to get one flavor right, +# the other one passed the test right away:-) +# +# sha256_block runs in ~1005 cycles on Opteron, which gives you +# asymptotic performance of 64*1000/1005=63.7MBps times CPU clock +# frequency in GHz. sha512_block runs in ~1275 cycles, which results +# in 128*1000/1275=100MBps per GHz. Is there room for improvement? +# Well, if you compare it to IA-64 implementation, which maintains +# X[16] in register bank[!], tends to 4 instructions per CPU clock +# cycle and runs in 1003 cycles, 1275 is very good result for 3-way +# issue Opteron pipeline and X[16] maintained in memory. So that *if* +# there is a way to improve it, *then* the only way would be to try to +# offload X[16] updates to SSE unit, but that would require "deeper" +# loop unroll, which in turn would naturally cause size blow-up, not +# to mention increased complexity! And once again, only *if* it's +# actually possible to noticeably improve overall ILP, instruction +# level parallelism, on a given CPU implementation in this case. +# +# Special note on Intel EM64T. While Opteron CPU exhibits perfect +# perfromance ratio of 1.5 between 64- and 32-bit flavors [see above], +# [currently available] EM64T CPUs apparently are far from it. On the +# contrary, 64-bit version, sha512_block, is ~30% *slower* than 32-bit +# sha256_block:-( This is presumably because 64-bit shifts/rotates +# apparently are not atomic instructions, but implemented in microcode. + +$output=shift; + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}x86_64-xlate.pl" and -f $xlate ) or +( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or +die "can't locate x86_64-xlate.pl"; + +open STDOUT,"| $^X $xlate $output"; + +if ($output =~ /512/) { + $func="sha512_block_data_order"; + $TABLE="K512"; + $SZ=8; + @ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%rax","%rbx","%rcx","%rdx", + "%r8", "%r9", "%r10","%r11"); + ($T1,$a0,$a1,$a2)=("%r12","%r13","%r14","%r15"); + @Sigma0=(28,34,39); + @Sigma1=(14,18,41); + @sigma0=(1, 8, 7); + @sigma1=(19,61, 6); + $rounds=80; +} else { + $func="sha256_block_data_order"; + $TABLE="K256"; + $SZ=4; + @ROT=($A,$B,$C,$D,$E,$F,$G,$H)=("%eax","%ebx","%ecx","%edx", + "%r8d","%r9d","%r10d","%r11d"); + ($T1,$a0,$a1,$a2)=("%r12d","%r13d","%r14d","%r15d"); + @Sigma0=( 2,13,22); + @Sigma1=( 6,11,25); + @sigma0=( 7,18, 3); + @sigma1=(17,19,10); + $rounds=64; +} + +$ctx="%rdi"; # 1st arg +$round="%rdi"; # zaps $ctx +$inp="%rsi"; # 2nd arg +$Tbl="%rbp"; + +$_ctx="16*$SZ+0*8(%rsp)"; +$_inp="16*$SZ+1*8(%rsp)"; +$_end="16*$SZ+2*8(%rsp)"; +$_rsp="16*$SZ+3*8(%rsp)"; +$framesz="16*$SZ+4*8"; + + +sub ROUND_00_15() +{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_; + +$code.=<<___; + mov $e,$a0 + mov $e,$a1 + mov $f,$a2 + + ror \$$Sigma1[0],$a0 + ror \$$Sigma1[1],$a1 + xor $g,$a2 # f^g + + xor $a1,$a0 + ror \$`$Sigma1[2]-$Sigma1[1]`,$a1 + and $e,$a2 # (f^g)&e + mov $T1,`$SZ*($i&0xf)`(%rsp) + + xor $a1,$a0 # Sigma1(e) + xor $g,$a2 # Ch(e,f,g)=((f^g)&e)^g + add $h,$T1 # T1+=h + + mov $a,$h + add $a0,$T1 # T1+=Sigma1(e) + + add $a2,$T1 # T1+=Ch(e,f,g) + mov $a,$a0 + mov $a,$a1 + + ror \$$Sigma0[0],$h + ror \$$Sigma0[1],$a0 + mov $a,$a2 + add ($Tbl,$round,$SZ),$T1 # T1+=K[round] + + xor $a0,$h + ror \$`$Sigma0[2]-$Sigma0[1]`,$a0 + or $c,$a1 # a|c + + xor $a0,$h # h=Sigma0(a) + and $c,$a2 # a&c + add $T1,$d # d+=T1 + + and $b,$a1 # (a|c)&b + add $T1,$h # h+=T1 + + or $a2,$a1 # Maj(a,b,c)=((a|c)&b)|(a&c) + lea 1($round),$round # round++ + + add $a1,$h # h+=Maj(a,b,c) +___ +} + +sub ROUND_16_XX() +{ my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_; + +$code.=<<___; + mov `$SZ*(($i+1)&0xf)`(%rsp),$a0 + mov `$SZ*(($i+14)&0xf)`(%rsp),$T1 + + mov $a0,$a2 + + shr \$$sigma0[2],$a0 + ror \$$sigma0[0],$a2 + + xor $a2,$a0 + ror \$`$sigma0[1]-$sigma0[0]`,$a2 + + xor $a2,$a0 # sigma0(X[(i+1)&0xf]) + mov $T1,$a1 + + shr \$$sigma1[2],$T1 + ror \$$sigma1[0],$a1 + + xor $a1,$T1 + ror \$`$sigma1[1]-$sigma1[0]`,$a1 + + xor $a1,$T1 # sigma1(X[(i+14)&0xf]) + + add $a0,$T1 + + add `$SZ*(($i+9)&0xf)`(%rsp),$T1 + + add `$SZ*($i&0xf)`(%rsp),$T1 +___ + &ROUND_00_15(@_); +} + +$code=<<___; +.text + +.globl $func +.type $func,\@function,4 +.align 16 +$func: + push %rbx + push %rbp + push %r12 + push %r13 + push %r14 + push %r15 + mov %rsp,%rbp # copy %rsp + shl \$4,%rdx # num*16 + sub \$$framesz,%rsp + lea ($inp,%rdx,$SZ),%rdx # inp+num*16*$SZ + and \$-64,%rsp # align stack frame + mov $ctx,$_ctx # save ctx, 1st arg + mov $inp,$_inp # save inp, 2nd arh + mov %rdx,$_end # save end pointer, "3rd" arg + mov %rbp,$_rsp # save copy of %rsp + + .picmeup $Tbl + lea $TABLE-.($Tbl),$Tbl + + mov $SZ*0($ctx),$A + mov $SZ*1($ctx),$B + mov $SZ*2($ctx),$C + mov $SZ*3($ctx),$D + mov $SZ*4($ctx),$E + mov $SZ*5($ctx),$F + mov $SZ*6($ctx),$G + mov $SZ*7($ctx),$H + jmp .Lloop + +.align 16 +.Lloop: + xor $round,$round +___ + for($i=0;$i<16;$i++) { + $code.=" mov $SZ*$i($inp),$T1\n"; + $code.=" bswap $T1\n"; + &ROUND_00_15($i,@ROT); + unshift(@ROT,pop(@ROT)); + } +$code.=<<___; + jmp .Lrounds_16_xx +.align 16 +.Lrounds_16_xx: +___ + for(;$i<32;$i++) { + &ROUND_16_XX($i,@ROT); + unshift(@ROT,pop(@ROT)); + } + +$code.=<<___; + cmp \$$rounds,$round + jb .Lrounds_16_xx + + mov $_ctx,$ctx + lea 16*$SZ($inp),$inp + + add $SZ*0($ctx),$A + add $SZ*1($ctx),$B + add $SZ*2($ctx),$C + add $SZ*3($ctx),$D + add $SZ*4($ctx),$E + add $SZ*5($ctx),$F + add $SZ*6($ctx),$G + add $SZ*7($ctx),$H + + cmp $_end,$inp + + mov $A,$SZ*0($ctx) + mov $B,$SZ*1($ctx) + mov $C,$SZ*2($ctx) + mov $D,$SZ*3($ctx) + mov $E,$SZ*4($ctx) + mov $F,$SZ*5($ctx) + mov $G,$SZ*6($ctx) + mov $H,$SZ*7($ctx) + jb .Lloop + + mov $_rsp,%rsp + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %rbp + pop %rbx + + ret +.size $func,.-$func +___ + +if ($SZ==4) { +$code.=<<___; +.align 64 +.type $TABLE,\@object +$TABLE: + .long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 + .long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 + .long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 + .long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 + .long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc + .long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da + .long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 + .long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 + .long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 + .long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 + .long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 + .long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 + .long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 + .long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 + .long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 + .long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +___ +} else { +$code.=<<___; +.align 64 +.type $TABLE,\@object +$TABLE: + .quad 0x428a2f98d728ae22,0x7137449123ef65cd + .quad 0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc + .quad 0x3956c25bf348b538,0x59f111f1b605d019 + .quad 0x923f82a4af194f9b,0xab1c5ed5da6d8118 + .quad 0xd807aa98a3030242,0x12835b0145706fbe + .quad 0x243185be4ee4b28c,0x550c7dc3d5ffb4e2 + .quad 0x72be5d74f27b896f,0x80deb1fe3b1696b1 + .quad 0x9bdc06a725c71235,0xc19bf174cf692694 + .quad 0xe49b69c19ef14ad2,0xefbe4786384f25e3 + .quad 0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65 + .quad 0x2de92c6f592b0275,0x4a7484aa6ea6e483 + .quad 0x5cb0a9dcbd41fbd4,0x76f988da831153b5 + .quad 0x983e5152ee66dfab,0xa831c66d2db43210 + .quad 0xb00327c898fb213f,0xbf597fc7beef0ee4 + .quad 0xc6e00bf33da88fc2,0xd5a79147930aa725 + .quad 0x06ca6351e003826f,0x142929670a0e6e70 + .quad 0x27b70a8546d22ffc,0x2e1b21385c26c926 + .quad 0x4d2c6dfc5ac42aed,0x53380d139d95b3df + .quad 0x650a73548baf63de,0x766a0abb3c77b2a8 + .quad 0x81c2c92e47edaee6,0x92722c851482353b + .quad 0xa2bfe8a14cf10364,0xa81a664bbc423001 + .quad 0xc24b8b70d0f89791,0xc76c51a30654be30 + .quad 0xd192e819d6ef5218,0xd69906245565a910 + .quad 0xf40e35855771202a,0x106aa07032bbd1b8 + .quad 0x19a4c116b8d2d0c8,0x1e376c085141ab53 + .quad 0x2748774cdf8eeb99,0x34b0bcb5e19b48a8 + .quad 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb + .quad 0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3 + .quad 0x748f82ee5defb2fc,0x78a5636f43172f60 + .quad 0x84c87814a1f0ab72,0x8cc702081a6439ec + .quad 0x90befffa23631e28,0xa4506cebde82bde9 + .quad 0xbef9a3f7b2c67915,0xc67178f2e372532b + .quad 0xca273eceea26619c,0xd186b8c721c0c207 + .quad 0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178 + .quad 0x06f067aa72176fba,0x0a637dc5a2c898a6 + .quad 0x113f9804bef90dae,0x1b710b35131c471b + .quad 0x28db77f523047d84,0x32caab7b40c72493 + .quad 0x3c9ebe0a15c9bebc,0x431d67c49c100d4c + .quad 0x4cc5d4becb3e42b6,0x597f299cfc657e2a + .quad 0x5fcb6fab3ad6faec,0x6c44198c4a475817 +___ +} + +$code =~ s/\`([^\`]*)\`/eval $1/gem; +print $code; +close STDOUT;