#! /usr/bin/env perl
# Copyright 2009-2020 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

# ====================================================================
# Written by Andy Polyakov <appro@openssl.org> 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/.
# ====================================================================

# SHA256/512 block procedure for PA-RISC.

# June 2009.
#
# SHA256 performance is >75% better than gcc 3.2 generated code on
# PA-7100LC. Compared to code generated by vendor compiler this
# implementation is almost 70% faster in 64-bit build, but delivers
# virtually same performance in 32-bit build on PA-8600.
#
# SHA512 performance is >2.9x better than gcc 3.2 generated code on
# PA-7100LC, PA-RISC 1.1 processor. Then implementation detects if the
# code is executed on PA-RISC 2.0 processor and switches to 64-bit
# code path delivering adequate performance even in "blended" 32-bit
# build. Though 64-bit code is not any faster than code generated by
# vendor compiler on PA-8600...
#
# Special thanks to polarhome.com for providing HP-UX account.

$flavour = shift;
$output = shift;
open STDOUT,">$output";

if ($flavour =~ /64/) {
	$LEVEL		="2.0W";
	$SIZE_T		=8;
	$FRAME_MARKER	=80;
	$SAVED_RP	=16;
	$PUSH		="std";
	$PUSHMA		="std,ma";
	$POP		="ldd";
	$POPMB		="ldd,mb";
} else {
	$LEVEL		="1.0";
	$SIZE_T		=4;
	$FRAME_MARKER	=48;
	$SAVED_RP	=20;
	$PUSH		="stw";
	$PUSHMA		="stwm";
	$POP		="ldw";
	$POPMB		="ldwm";
}

if ($output =~ /512/) {
	$func="sha512_block_data_order";
	$SZ=8;
	@Sigma0=(28,34,39);
	@Sigma1=(14,18,41);
	@sigma0=(1,  8, 7);
	@sigma1=(19,61, 6);
	$rounds=80;
	$LAST10BITS=0x017;
	$LD="ldd";
	$LDM="ldd,ma";
	$ST="std";
} else {
	$func="sha256_block_data_order";
	$SZ=4;
	@Sigma0=( 2,13,22);
	@Sigma1=( 6,11,25);
	@sigma0=( 7,18, 3);
	@sigma1=(17,19,10);
	$rounds=64;
	$LAST10BITS=0x0f2;
	$LD="ldw";
	$LDM="ldwm";
	$ST="stw";
}

$FRAME=16*$SIZE_T+$FRAME_MARKER;# 16 saved regs + frame marker
				#                 [+ argument transfer]
$XOFF=16*$SZ+32;		# local variables
$FRAME+=$XOFF;
$XOFF+=$FRAME_MARKER;		# distance between %sp and local variables

$ctx="%r26";	# zapped by $a0
$inp="%r25";	# zapped by $a1
$num="%r24";	# zapped by $t0

$a0 ="%r26";
$a1 ="%r25";
$t0 ="%r24";
$t1 ="%r29";
$Tbl="%r31";

@V=($A,$B,$C,$D,$E,$F,$G,$H)=("%r17","%r18","%r19","%r20","%r21","%r22","%r23","%r28");

@X=("%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8",
    "%r9", "%r10","%r11","%r12","%r13","%r14","%r15","%r16",$inp);

sub ROUND_00_15 {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
$code.=<<___;
	_ror	$e,$Sigma1[0],$a0
	and	$f,$e,$t0
	_ror	$e,$Sigma1[1],$a1
	addl	$t1,$h,$h
	andcm	$g,$e,$t1
	xor	$a1,$a0,$a0
	_ror	$a1,`$Sigma1[2]-$Sigma1[1]`,$a1
	or	$t0,$t1,$t1		; Ch(e,f,g)
	addl	@X[$i%16],$h,$h
	xor	$a0,$a1,$a1		; Sigma1(e)
	addl	$t1,$h,$h
	_ror	$a,$Sigma0[0],$a0
	addl	$a1,$h,$h

	_ror	$a,$Sigma0[1],$a1
	and	$a,$b,$t0
	and	$a,$c,$t1
	xor	$a1,$a0,$a0
	_ror	$a1,`$Sigma0[2]-$Sigma0[1]`,$a1
	xor	$t1,$t0,$t0
	and	$b,$c,$t1
	xor	$a0,$a1,$a1		; Sigma0(a)
	addl	$h,$d,$d
	xor	$t1,$t0,$t0		; Maj(a,b,c)
	`"$LDM	$SZ($Tbl),$t1" if ($i<15)`
	addl	$a1,$h,$h
	addl	$t0,$h,$h

___
}

sub ROUND_16_xx {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
$i-=16;
$code.=<<___;
	_ror	@X[($i+1)%16],$sigma0[0],$a0
	_ror	@X[($i+1)%16],$sigma0[1],$a1
	addl	@X[($i+9)%16],@X[$i],@X[$i]
	_ror	@X[($i+14)%16],$sigma1[0],$t0
	_ror	@X[($i+14)%16],$sigma1[1],$t1
	xor	$a1,$a0,$a0
	_shr	@X[($i+1)%16],$sigma0[2],$a1
	xor	$t1,$t0,$t0
	_shr	@X[($i+14)%16],$sigma1[2],$t1
	xor	$a1,$a0,$a0		; sigma0(X[(i+1)&0x0f])
	xor	$t1,$t0,$t0		; sigma1(X[(i+14)&0x0f])
	$LDM	$SZ($Tbl),$t1
	addl	$a0,@X[$i],@X[$i]
	addl	$t0,@X[$i],@X[$i]
___
$code.=<<___ if ($i==15);
	extru	$t1,31,10,$a1
	comiclr,<> $LAST10BITS,$a1,%r0
	ldo	1($Tbl),$Tbl		; signal end of $Tbl
___
&ROUND_00_15($i+16,$a,$b,$c,$d,$e,$f,$g,$h);
}

$code=<<___;
	.LEVEL	$LEVEL
	.SPACE	\$TEXT\$
	.SUBSPA	\$CODE\$,QUAD=0,ALIGN=8,ACCESS=0x2C,CODE_ONLY

	.ALIGN	64
L\$table
___
$code.=<<___ if ($SZ==8);
	.WORD	0x428a2f98,0xd728ae22,0x71374491,0x23ef65cd
	.WORD	0xb5c0fbcf,0xec4d3b2f,0xe9b5dba5,0x8189dbbc
	.WORD	0x3956c25b,0xf348b538,0x59f111f1,0xb605d019
	.WORD	0x923f82a4,0xaf194f9b,0xab1c5ed5,0xda6d8118
	.WORD	0xd807aa98,0xa3030242,0x12835b01,0x45706fbe
	.WORD	0x243185be,0x4ee4b28c,0x550c7dc3,0xd5ffb4e2
	.WORD	0x72be5d74,0xf27b896f,0x80deb1fe,0x3b1696b1
	.WORD	0x9bdc06a7,0x25c71235,0xc19bf174,0xcf692694
	.WORD	0xe49b69c1,0x9ef14ad2,0xefbe4786,0x384f25e3
	.WORD	0x0fc19dc6,0x8b8cd5b5,0x240ca1cc,0x77ac9c65
	.WORD	0x2de92c6f,0x592b0275,0x4a7484aa,0x6ea6e483
	.WORD	0x5cb0a9dc,0xbd41fbd4,0x76f988da,0x831153b5
	.WORD	0x983e5152,0xee66dfab,0xa831c66d,0x2db43210
	.WORD	0xb00327c8,0x98fb213f,0xbf597fc7,0xbeef0ee4
	.WORD	0xc6e00bf3,0x3da88fc2,0xd5a79147,0x930aa725
	.WORD	0x06ca6351,0xe003826f,0x14292967,0x0a0e6e70
	.WORD	0x27b70a85,0x46d22ffc,0x2e1b2138,0x5c26c926
	.WORD	0x4d2c6dfc,0x5ac42aed,0x53380d13,0x9d95b3df
	.WORD	0x650a7354,0x8baf63de,0x766a0abb,0x3c77b2a8
	.WORD	0x81c2c92e,0x47edaee6,0x92722c85,0x1482353b
	.WORD	0xa2bfe8a1,0x4cf10364,0xa81a664b,0xbc423001
	.WORD	0xc24b8b70,0xd0f89791,0xc76c51a3,0x0654be30
	.WORD	0xd192e819,0xd6ef5218,0xd6990624,0x5565a910
	.WORD	0xf40e3585,0x5771202a,0x106aa070,0x32bbd1b8
	.WORD	0x19a4c116,0xb8d2d0c8,0x1e376c08,0x5141ab53
	.WORD	0x2748774c,0xdf8eeb99,0x34b0bcb5,0xe19b48a8
	.WORD	0x391c0cb3,0xc5c95a63,0x4ed8aa4a,0xe3418acb
	.WORD	0x5b9cca4f,0x7763e373,0x682e6ff3,0xd6b2b8a3
	.WORD	0x748f82ee,0x5defb2fc,0x78a5636f,0x43172f60
	.WORD	0x84c87814,0xa1f0ab72,0x8cc70208,0x1a6439ec
	.WORD	0x90befffa,0x23631e28,0xa4506ceb,0xde82bde9
	.WORD	0xbef9a3f7,0xb2c67915,0xc67178f2,0xe372532b
	.WORD	0xca273ece,0xea26619c,0xd186b8c7,0x21c0c207
	.WORD	0xeada7dd6,0xcde0eb1e,0xf57d4f7f,0xee6ed178
	.WORD	0x06f067aa,0x72176fba,0x0a637dc5,0xa2c898a6
	.WORD	0x113f9804,0xbef90dae,0x1b710b35,0x131c471b
	.WORD	0x28db77f5,0x23047d84,0x32caab7b,0x40c72493
	.WORD	0x3c9ebe0a,0x15c9bebc,0x431d67c4,0x9c100d4c
	.WORD	0x4cc5d4be,0xcb3e42b6,0x597f299c,0xfc657e2a
	.WORD	0x5fcb6fab,0x3ad6faec,0x6c44198c,0x4a475817
___
$code.=<<___ if ($SZ==4);
	.WORD	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
	.WORD	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
	.WORD	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
	.WORD	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
	.WORD	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
	.WORD	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
	.WORD	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
	.WORD	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
	.WORD	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
	.WORD	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
	.WORD	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
	.WORD	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
	.WORD	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
	.WORD	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
	.WORD	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
	.WORD	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
___
$code.=<<___;

	.EXPORT	$func,ENTRY,ARGW0=GR,ARGW1=GR,ARGW2=GR
	.ALIGN	64
$func
	.PROC
	.CALLINFO	FRAME=`$FRAME-16*$SIZE_T`,NO_CALLS,SAVE_RP,ENTRY_GR=18
	.ENTRY
	$PUSH	%r2,-$SAVED_RP(%sp)	; standard prologue
	$PUSHMA	%r3,$FRAME(%sp)
	$PUSH	%r4,`-$FRAME+1*$SIZE_T`(%sp)
	$PUSH	%r5,`-$FRAME+2*$SIZE_T`(%sp)
	$PUSH	%r6,`-$FRAME+3*$SIZE_T`(%sp)
	$PUSH	%r7,`-$FRAME+4*$SIZE_T`(%sp)
	$PUSH	%r8,`-$FRAME+5*$SIZE_T`(%sp)
	$PUSH	%r9,`-$FRAME+6*$SIZE_T`(%sp)
	$PUSH	%r10,`-$FRAME+7*$SIZE_T`(%sp)
	$PUSH	%r11,`-$FRAME+8*$SIZE_T`(%sp)
	$PUSH	%r12,`-$FRAME+9*$SIZE_T`(%sp)
	$PUSH	%r13,`-$FRAME+10*$SIZE_T`(%sp)
	$PUSH	%r14,`-$FRAME+11*$SIZE_T`(%sp)
	$PUSH	%r15,`-$FRAME+12*$SIZE_T`(%sp)
	$PUSH	%r16,`-$FRAME+13*$SIZE_T`(%sp)
	$PUSH	%r17,`-$FRAME+14*$SIZE_T`(%sp)
	$PUSH	%r18,`-$FRAME+15*$SIZE_T`(%sp)

	_shl	$num,`log(16*$SZ)/log(2)`,$num
	addl	$inp,$num,$num		; $num to point at the end of $inp

	$PUSH	$num,`-$FRAME_MARKER-4*$SIZE_T`(%sp)	; save arguments
	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)
	$PUSH	$ctx,`-$FRAME_MARKER-2*$SIZE_T`(%sp)

	blr	%r0,$Tbl
	ldi	3,$t1
L\$pic
	andcm	$Tbl,$t1,$Tbl		; wipe privilege level
	ldo	L\$table-L\$pic($Tbl),$Tbl
___
$code.=<<___ if ($SZ==8 && $SIZE_T==4);
	ldi	31,$t1
	mtctl	$t1,%cr11
	extrd,u,*= $t1,%sar,1,$t1	; executes on PA-RISC 1.0
	b	L\$parisc1
	nop
___
$code.=<<___;
	$LD	`0*$SZ`($ctx),$A	; load context
	$LD	`1*$SZ`($ctx),$B
	$LD	`2*$SZ`($ctx),$C
	$LD	`3*$SZ`($ctx),$D
	$LD	`4*$SZ`($ctx),$E
	$LD	`5*$SZ`($ctx),$F
	$LD	`6*$SZ`($ctx),$G
	$LD	`7*$SZ`($ctx),$H

	extru	$inp,31,`log($SZ)/log(2)`,$t0
	sh3addl	$t0,%r0,$t0
	subi	`8*$SZ`,$t0,$t0
	mtctl	$t0,%cr11		; load %sar with align factor

L\$oop
	ldi	`$SZ-1`,$t0
	$LDM	$SZ($Tbl),$t1
	andcm	$inp,$t0,$t0		; align $inp
___
	for ($i=0;$i<15;$i++) {		# load input block
	$code.="\t$LD	`$SZ*$i`($t0),@X[$i]\n";		}
$code.=<<___;
	cmpb,*=	$inp,$t0,L\$aligned
	$LD	`$SZ*15`($t0),@X[15]
	$LD	`$SZ*16`($t0),@X[16]
___
	for ($i=0;$i<16;$i++) {		# align data
	$code.="\t_align	@X[$i],@X[$i+1],@X[$i]\n";	}
$code.=<<___;
L\$aligned
	nop	; otherwise /usr/ccs/bin/as is confused by below .WORD
___

for($i=0;$i<16;$i++)	{ &ROUND_00_15($i,@V); unshift(@V,pop(@V)); }
$code.=<<___;
L\$rounds
	nop	; otherwise /usr/ccs/bin/as is confused by below .WORD
___
for(;$i<32;$i++)	{ &ROUND_16_xx($i,@V); unshift(@V,pop(@V)); }
$code.=<<___;
	bb,>=	$Tbl,31,L\$rounds	; end of $Tbl signalled?
	nop

	$POP	`-$FRAME_MARKER-2*$SIZE_T`(%sp),$ctx	; restore arguments
	$POP	`-$FRAME_MARKER-3*$SIZE_T`(%sp),$inp
	$POP	`-$FRAME_MARKER-4*$SIZE_T`(%sp),$num
	ldo	`-$rounds*$SZ-1`($Tbl),$Tbl		; rewind $Tbl

	$LD	`0*$SZ`($ctx),@X[0]	; load context
	$LD	`1*$SZ`($ctx),@X[1]
	$LD	`2*$SZ`($ctx),@X[2]
	$LD	`3*$SZ`($ctx),@X[3]
	$LD	`4*$SZ`($ctx),@X[4]
	$LD	`5*$SZ`($ctx),@X[5]
	addl	@X[0],$A,$A
	$LD	`6*$SZ`($ctx),@X[6]
	addl	@X[1],$B,$B
	$LD	`7*$SZ`($ctx),@X[7]
	ldo	`16*$SZ`($inp),$inp	; advance $inp

	$ST	$A,`0*$SZ`($ctx)	; save context
	addl	@X[2],$C,$C
	$ST	$B,`1*$SZ`($ctx)
	addl	@X[3],$D,$D
	$ST	$C,`2*$SZ`($ctx)
	addl	@X[4],$E,$E
	$ST	$D,`3*$SZ`($ctx)
	addl	@X[5],$F,$F
	$ST	$E,`4*$SZ`($ctx)
	addl	@X[6],$G,$G
	$ST	$F,`5*$SZ`($ctx)
	addl	@X[7],$H,$H
	$ST	$G,`6*$SZ`($ctx)
	$ST	$H,`7*$SZ`($ctx)

	cmpb,*<>,n $inp,$num,L\$oop
	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)	; save $inp
___
if ($SZ==8 && $SIZE_T==4)	# SHA512 for 32-bit PA-RISC 1.0
{{
$code.=<<___;
	b	L\$done
	nop

	.ALIGN	64
L\$parisc1
___

@V=(  $Ahi,  $Alo,  $Bhi,  $Blo,  $Chi,  $Clo,  $Dhi,  $Dlo,
      $Ehi,  $Elo,  $Fhi,  $Flo,  $Ghi,  $Glo,  $Hhi,  $Hlo) =
   ( "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", "%r8",
     "%r9","%r10","%r11","%r12","%r13","%r14","%r15","%r16");
$a0 ="%r17";
$a1 ="%r18";
$a2 ="%r19";
$a3 ="%r20";
$t0 ="%r21";
$t1 ="%r22";
$t2 ="%r28";
$t3 ="%r29";
$Tbl="%r31";

@X=("%r23","%r24","%r25","%r26");	# zaps $num,$inp,$ctx

sub ROUND_00_15_pa1 {
my ($i,$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
       $ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo,$flag)=@_;
my ($Xhi,$Xlo,$Xnhi,$Xnlo) = @X;

$code.=<<___ if (!$flag);
	ldw	`-$XOFF+8*(($i+1)%16)`(%sp),$Xnhi
	ldw	`-$XOFF+8*(($i+1)%16)+4`(%sp),$Xnlo	; load X[i+1]
___
$code.=<<___;
	shd	$ehi,$elo,$Sigma1[0],$t0
	 add	$Xlo,$hlo,$hlo
	shd	$elo,$ehi,$Sigma1[0],$t1
	 addc	$Xhi,$hhi,$hhi		; h += X[i]
	shd	$ehi,$elo,$Sigma1[1],$t2
	 ldwm	8($Tbl),$Xhi
	shd	$elo,$ehi,$Sigma1[1],$t3
	 ldw	-4($Tbl),$Xlo		; load K[i]
	xor	$t2,$t0,$t0
	xor	$t3,$t1,$t1
	 and	$flo,$elo,$a0
	 and	$fhi,$ehi,$a1
	shd	$ehi,$elo,$Sigma1[2],$t2
	 andcm	$glo,$elo,$a2
	shd	$elo,$ehi,$Sigma1[2],$t3
	 andcm	$ghi,$ehi,$a3
	xor	$t2,$t0,$t0
	xor	$t3,$t1,$t1		; Sigma1(e)
	add	$Xlo,$hlo,$hlo
	 xor	$a2,$a0,$a0
	addc	$Xhi,$hhi,$hhi		; h += K[i]
	 xor	$a3,$a1,$a1		; Ch(e,f,g)

	 add	$t0,$hlo,$hlo
	shd	$ahi,$alo,$Sigma0[0],$t0
	 addc	$t1,$hhi,$hhi		; h += Sigma1(e)
	shd	$alo,$ahi,$Sigma0[0],$t1
	 add	$a0,$hlo,$hlo
	shd	$ahi,$alo,$Sigma0[1],$t2
	 addc	$a1,$hhi,$hhi		; h += Ch(e,f,g)
	shd	$alo,$ahi,$Sigma0[1],$t3

	xor	$t2,$t0,$t0
	xor	$t3,$t1,$t1
	shd	$ahi,$alo,$Sigma0[2],$t2
	and	$alo,$blo,$a0
	shd	$alo,$ahi,$Sigma0[2],$t3
	and	$ahi,$bhi,$a1
	xor	$t2,$t0,$t0
	xor	$t3,$t1,$t1		; Sigma0(a)

	and	$alo,$clo,$a2
	and	$ahi,$chi,$a3
	xor	$a2,$a0,$a0
	 add	$hlo,$dlo,$dlo
	xor	$a3,$a1,$a1
	 addc	$hhi,$dhi,$dhi		; d += h
	and	$blo,$clo,$a2
	 add	$t0,$hlo,$hlo
	and	$bhi,$chi,$a3
	 addc	$t1,$hhi,$hhi		; h += Sigma0(a)
	xor	$a2,$a0,$a0
	 add	$a0,$hlo,$hlo
	xor	$a3,$a1,$a1		; Maj(a,b,c)
	 addc	$a1,$hhi,$hhi		; h += Maj(a,b,c)

___
$code.=<<___ if ($i==15 && $flag);
	extru	$Xlo,31,10,$Xlo
	comiclr,= $LAST10BITS,$Xlo,%r0
	b	L\$rounds_pa1
	nop
___
push(@X,shift(@X)); push(@X,shift(@X));
}

sub ROUND_16_xx_pa1 {
my ($Xhi,$Xlo,$Xnhi,$Xnlo) = @X;
my ($i)=shift;
$i-=16;
$code.=<<___;
	ldw	`-$XOFF+8*(($i+1)%16)`(%sp),$Xnhi
	ldw	`-$XOFF+8*(($i+1)%16)+4`(%sp),$Xnlo	; load X[i+1]
	ldw	`-$XOFF+8*(($i+9)%16)`(%sp),$a1
	ldw	`-$XOFF+8*(($i+9)%16)+4`(%sp),$a0	; load X[i+9]
	ldw	`-$XOFF+8*(($i+14)%16)`(%sp),$a3
	ldw	`-$XOFF+8*(($i+14)%16)+4`(%sp),$a2	; load X[i+14]
	shd	$Xnhi,$Xnlo,$sigma0[0],$t0
	shd	$Xnlo,$Xnhi,$sigma0[0],$t1
	 add	$a0,$Xlo,$Xlo
	shd	$Xnhi,$Xnlo,$sigma0[1],$t2
	 addc	$a1,$Xhi,$Xhi
	shd	$Xnlo,$Xnhi,$sigma0[1],$t3
	xor	$t2,$t0,$t0
	shd	$Xnhi,$Xnlo,$sigma0[2],$t2
	xor	$t3,$t1,$t1
	extru	$Xnhi,`31-$sigma0[2]`,`32-$sigma0[2]`,$t3
	xor	$t2,$t0,$t0
	 shd	$a3,$a2,$sigma1[0],$a0
	xor	$t3,$t1,$t1		; sigma0(X[i+1)&0x0f])
	 shd	$a2,$a3,$sigma1[0],$a1
	add	$t0,$Xlo,$Xlo
	 shd	$a3,$a2,$sigma1[1],$t2
	addc	$t1,$Xhi,$Xhi
	 shd	$a2,$a3,$sigma1[1],$t3
	xor	$t2,$a0,$a0
	shd	$a3,$a2,$sigma1[2],$t2
	xor	$t3,$a1,$a1
	extru	$a3,`31-$sigma1[2]`,`32-$sigma1[2]`,$t3
	xor	$t2,$a0,$a0
	xor	$t3,$a1,$a1		; sigma0(X[i+14)&0x0f])
	add	$a0,$Xlo,$Xlo
	addc	$a1,$Xhi,$Xhi

	stw	$Xhi,`-$XOFF+8*($i%16)`(%sp)
	stw	$Xlo,`-$XOFF+8*($i%16)+4`(%sp)
___
&ROUND_00_15_pa1($i,@_,1);
}
$code.=<<___;
	ldw	`0*4`($ctx),$Ahi		; load context
	ldw	`1*4`($ctx),$Alo
	ldw	`2*4`($ctx),$Bhi
	ldw	`3*4`($ctx),$Blo
	ldw	`4*4`($ctx),$Chi
	ldw	`5*4`($ctx),$Clo
	ldw	`6*4`($ctx),$Dhi
	ldw	`7*4`($ctx),$Dlo
	ldw	`8*4`($ctx),$Ehi
	ldw	`9*4`($ctx),$Elo
	ldw	`10*4`($ctx),$Fhi
	ldw	`11*4`($ctx),$Flo
	ldw	`12*4`($ctx),$Ghi
	ldw	`13*4`($ctx),$Glo
	ldw	`14*4`($ctx),$Hhi
	ldw	`15*4`($ctx),$Hlo

	extru	$inp,31,2,$t0
	sh3addl	$t0,%r0,$t0
	subi	32,$t0,$t0
	mtctl	$t0,%cr11		; load %sar with align factor

L\$oop_pa1
	extru	$inp,31,2,$a3
	comib,=	0,$a3,L\$aligned_pa1
	sub	$inp,$a3,$inp

	ldw	`0*4`($inp),$X[0]
	ldw	`1*4`($inp),$X[1]
	ldw	`2*4`($inp),$t2
	ldw	`3*4`($inp),$t3
	ldw	`4*4`($inp),$a0
	ldw	`5*4`($inp),$a1
	ldw	`6*4`($inp),$a2
	ldw	`7*4`($inp),$a3
	vshd	$X[0],$X[1],$X[0]
	vshd	$X[1],$t2,$X[1]
	stw	$X[0],`-$XOFF+0*4`(%sp)
	ldw	`8*4`($inp),$t0
	vshd	$t2,$t3,$t2
	stw	$X[1],`-$XOFF+1*4`(%sp)
	ldw	`9*4`($inp),$t1
	vshd	$t3,$a0,$t3
___
{
my @t=($t2,$t3,$a0,$a1,$a2,$a3,$t0,$t1);
for ($i=2;$i<=(128/4-8);$i++) {
$code.=<<___;
	stw	$t[0],`-$XOFF+$i*4`(%sp)
	ldw	`(8+$i)*4`($inp),$t[0]
	vshd	$t[1],$t[2],$t[1]
___
push(@t,shift(@t));
}
for (;$i<(128/4-1);$i++) {
$code.=<<___;
	stw	$t[0],`-$XOFF+$i*4`(%sp)
	vshd	$t[1],$t[2],$t[1]
___
push(@t,shift(@t));
}
$code.=<<___;
	b	L\$collected_pa1
	stw	$t[0],`-$XOFF+$i*4`(%sp)

___
}
$code.=<<___;
L\$aligned_pa1
	ldw	`0*4`($inp),$X[0]
	ldw	`1*4`($inp),$X[1]
	ldw	`2*4`($inp),$t2
	ldw	`3*4`($inp),$t3
	ldw	`4*4`($inp),$a0
	ldw	`5*4`($inp),$a1
	ldw	`6*4`($inp),$a2
	ldw	`7*4`($inp),$a3
	stw	$X[0],`-$XOFF+0*4`(%sp)
	ldw	`8*4`($inp),$t0
	stw	$X[1],`-$XOFF+1*4`(%sp)
	ldw	`9*4`($inp),$t1
___
{
my @t=($t2,$t3,$a0,$a1,$a2,$a3,$t0,$t1);
for ($i=2;$i<(128/4-8);$i++) {
$code.=<<___;
	stw	$t[0],`-$XOFF+$i*4`(%sp)
	ldw	`(8+$i)*4`($inp),$t[0]
___
push(@t,shift(@t));
}
for (;$i<128/4;$i++) {
$code.=<<___;
	stw	$t[0],`-$XOFF+$i*4`(%sp)
___
push(@t,shift(@t));
}
$code.="L\$collected_pa1\n";
}

for($i=0;$i<16;$i++)	{ &ROUND_00_15_pa1($i,@V); unshift(@V,pop(@V)); unshift(@V,pop(@V)); }
$code.="L\$rounds_pa1\n";
for(;$i<32;$i++)	{ &ROUND_16_xx_pa1($i,@V); unshift(@V,pop(@V)); unshift(@V,pop(@V)); }

$code.=<<___;
	$POP	`-$FRAME_MARKER-2*$SIZE_T`(%sp),$ctx	; restore arguments
	$POP	`-$FRAME_MARKER-3*$SIZE_T`(%sp),$inp
	$POP	`-$FRAME_MARKER-4*$SIZE_T`(%sp),$num
	ldo	`-$rounds*$SZ`($Tbl),$Tbl		; rewind $Tbl

	ldw	`0*4`($ctx),$t1		; update context
	ldw	`1*4`($ctx),$t0
	ldw	`2*4`($ctx),$t3
	ldw	`3*4`($ctx),$t2
	ldw	`4*4`($ctx),$a1
	ldw	`5*4`($ctx),$a0
	ldw	`6*4`($ctx),$a3
	add	$t0,$Alo,$Alo
	ldw	`7*4`($ctx),$a2
	addc	$t1,$Ahi,$Ahi
	ldw	`8*4`($ctx),$t1
	add	$t2,$Blo,$Blo
	ldw	`9*4`($ctx),$t0
	addc	$t3,$Bhi,$Bhi
	ldw	`10*4`($ctx),$t3
	add	$a0,$Clo,$Clo
	ldw	`11*4`($ctx),$t2
	addc	$a1,$Chi,$Chi
	ldw	`12*4`($ctx),$a1
	add	$a2,$Dlo,$Dlo
	ldw	`13*4`($ctx),$a0
	addc	$a3,$Dhi,$Dhi
	ldw	`14*4`($ctx),$a3
	add	$t0,$Elo,$Elo
	ldw	`15*4`($ctx),$a2
	addc	$t1,$Ehi,$Ehi
	stw	$Ahi,`0*4`($ctx)
	add	$t2,$Flo,$Flo
	stw	$Alo,`1*4`($ctx)
	addc	$t3,$Fhi,$Fhi
	stw	$Bhi,`2*4`($ctx)
	add	$a0,$Glo,$Glo
	stw	$Blo,`3*4`($ctx)
	addc	$a1,$Ghi,$Ghi
	stw	$Chi,`4*4`($ctx)
	add	$a2,$Hlo,$Hlo
	stw	$Clo,`5*4`($ctx)
	addc	$a3,$Hhi,$Hhi
	stw	$Dhi,`6*4`($ctx)
	ldo	`16*$SZ`($inp),$inp	; advance $inp
	stw	$Dlo,`7*4`($ctx)
	stw	$Ehi,`8*4`($ctx)
	stw	$Elo,`9*4`($ctx)
	stw	$Fhi,`10*4`($ctx)
	stw	$Flo,`11*4`($ctx)
	stw	$Ghi,`12*4`($ctx)
	stw	$Glo,`13*4`($ctx)
	stw	$Hhi,`14*4`($ctx)
	comb,=	$inp,$num,L\$done
	stw	$Hlo,`15*4`($ctx)
	b	L\$oop_pa1
	$PUSH	$inp,`-$FRAME_MARKER-3*$SIZE_T`(%sp)	; save $inp
L\$done
___
}}
$code.=<<___;
	$POP	`-$FRAME-$SAVED_RP`(%sp),%r2		; standard epilogue
	$POP	`-$FRAME+1*$SIZE_T`(%sp),%r4
	$POP	`-$FRAME+2*$SIZE_T`(%sp),%r5
	$POP	`-$FRAME+3*$SIZE_T`(%sp),%r6
	$POP	`-$FRAME+4*$SIZE_T`(%sp),%r7
	$POP	`-$FRAME+5*$SIZE_T`(%sp),%r8
	$POP	`-$FRAME+6*$SIZE_T`(%sp),%r9
	$POP	`-$FRAME+7*$SIZE_T`(%sp),%r10
	$POP	`-$FRAME+8*$SIZE_T`(%sp),%r11
	$POP	`-$FRAME+9*$SIZE_T`(%sp),%r12
	$POP	`-$FRAME+10*$SIZE_T`(%sp),%r13
	$POP	`-$FRAME+11*$SIZE_T`(%sp),%r14
	$POP	`-$FRAME+12*$SIZE_T`(%sp),%r15
	$POP	`-$FRAME+13*$SIZE_T`(%sp),%r16
	$POP	`-$FRAME+14*$SIZE_T`(%sp),%r17
	$POP	`-$FRAME+15*$SIZE_T`(%sp),%r18
	bv	(%r2)
	.EXIT
	$POPMB	-$FRAME(%sp),%r3
	.PROCEND
	.STRINGZ "SHA`64*$SZ` block transform for PA-RISC, CRYPTOGAMS by <appro\@openssl.org>"
___

# Explicitly encode PA-RISC 2.0 instructions used in this module, so
# that it can be compiled with .LEVEL 1.0. It should be noted that I
# wouldn't have to do this, if GNU assembler understood .ALLOW 2.0
# directive...

my $ldd = sub {
  my ($mod,$args) = @_;
  my $orig = "ldd$mod\t$args";

    if ($args =~ /(\-?[0-9]+)\(%r([0-9]+)\),%r([0-9]+)/) # format 3 suffices
    {	my $opcode=(0x14<<26)|($2<<21)|($3<<16)|(($1&0x1FF8)<<1)|(($1>>13)&1);
	$opcode|=(1<<3) if ($mod =~ /^,m/);
	$opcode|=(1<<2) if ($mod =~ /^,mb/);
	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
    }
    else { "\t".$orig; }
};

my $std = sub {
  my ($mod,$args) = @_;
  my $orig = "std$mod\t$args";

    if ($args =~ /%r([0-9]+),(\-?[0-9]+)\(%r([0-9]+)\)/) # format 3 suffices
    {	my $opcode=(0x1c<<26)|($3<<21)|($1<<16)|(($2&0x1FF8)<<1)|(($2>>13)&1);
	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
    }
    else { "\t".$orig; }
};

my $extrd = sub {
  my ($mod,$args) = @_;
  my $orig = "extrd$mod\t$args";

    # I only have ",u" completer, it's implicitly encoded...
    if ($args =~ /%r([0-9]+),([0-9]+),([0-9]+),%r([0-9]+)/)	# format 15
    {	my $opcode=(0x36<<26)|($1<<21)|($4<<16);
	my $len=32-$3;
	$opcode |= (($2&0x20)<<6)|(($2&0x1f)<<5);		# encode pos
	$opcode |= (($len&0x20)<<7)|($len&0x1f);		# encode len
	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
    }
    elsif ($args =~ /%r([0-9]+),%sar,([0-9]+),%r([0-9]+)/)	# format 12
    {	my $opcode=(0x34<<26)|($1<<21)|($3<<16)|(2<<11)|(1<<9);
	my $len=32-$2;
	$opcode |= (($len&0x20)<<3)|($len&0x1f);		# encode len
	$opcode |= (1<<13) if ($mod =~ /,\**=/);
	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
    }
    else { "\t".$orig; }
};

my $shrpd = sub {
  my ($mod,$args) = @_;
  my $orig = "shrpd$mod\t$args";

    if ($args =~ /%r([0-9]+),%r([0-9]+),([0-9]+),%r([0-9]+)/)	# format 14
    {	my $opcode=(0x34<<26)|($2<<21)|($1<<16)|(1<<10)|$4;
	my $cpos=63-$3;
	$opcode |= (($cpos&0x20)<<6)|(($cpos&0x1f)<<5);		# encode sa
	sprintf "\t.WORD\t0x%08x\t; %s",$opcode,$orig;
    }
    elsif ($args =~ /%r([0-9]+),%r([0-9]+),%sar,%r([0-9]+)/)	# format 11
    {	sprintf "\t.WORD\t0x%08x\t; %s",
		(0x34<<26)|($2<<21)|($1<<16)|(1<<9)|$3,$orig;
    }
    else { "\t".$orig; }
};

sub assemble {
  my ($mnemonic,$mod,$args)=@_;
  my $opcode = eval("\$$mnemonic");

    ref($opcode) eq 'CODE' ? &$opcode($mod,$args) : "\t$mnemonic$mod\t$args";
}

if (`$ENV{CC} -Wa,-v -c -o /dev/null -x assembler /dev/null 2>&1`
	=~ /GNU assembler/) {
    $gnuas = 1;
}

foreach (split("\n",$code)) {
	s/\`([^\`]*)\`/eval $1/ge;

	s/shd\s+(%r[0-9]+),(%r[0-9]+),([0-9]+)/
		$3>31 ? sprintf("shd\t%$2,%$1,%d",$3-32)	# rotation for >=32
		:       sprintf("shd\t%$1,%$2,%d",$3)/e			or
	# translate made up instructions: _ror, _shr, _align, _shl
	s/_ror(\s+)(%r[0-9]+),/
		($SZ==4 ? "shd" : "shrpd")."$1$2,$2,"/e			or

	s/_shr(\s+%r[0-9]+),([0-9]+),/
		$SZ==4 ? sprintf("extru%s,%d,%d,",$1,31-$2,32-$2)
		:        sprintf("extrd,u%s,%d,%d,",$1,63-$2,64-$2)/e	or

	s/_align(\s+%r[0-9]+,%r[0-9]+),/
		($SZ==4 ? "vshd$1," : "shrpd$1,%sar,")/e		or

	s/_shl(\s+%r[0-9]+),([0-9]+),/
		$SIZE_T==4 ? sprintf("zdep%s,%d,%d,",$1,31-$2,32-$2)
		:            sprintf("depd,z%s,%d,%d,",$1,63-$2,64-$2)/e;

	s/^\s+([a-z]+)([\S]*)\s+([\S]*)/&assemble($1,$2,$3)/e if ($SIZE_T==4);

	s/(\.LEVEL\s+2\.0)W/$1w/	if ($gnuas && $SIZE_T==8);
	s/\.SPACE\s+\$TEXT\$/.text/	if ($gnuas && $SIZE_T==8);
	s/\.SUBSPA.*//			if ($gnuas && $SIZE_T==8);
	s/cmpb,\*/comb,/ 		if ($SIZE_T==4);
	s/\bbv\b/bve/    		if ($SIZE_T==8);

	print $_,"\n";
}

close STDOUT or die "error closing STDOUT: $!";


#! /usr/bin/env perl
# Copyright 2006-2020 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

# ====================================================================
# Written by Andy Polyakov <appro@openssl.org> 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/.
# ====================================================================

# I let hardware handle unaligned input, except on page boundaries
# (see below for details). Otherwise straightforward implementation
# with X vector in register bank.

#			sha256		|	sha512
# 			-m64	-m32	|	-m64	-m32
# --------------------------------------+-----------------------
# PPC970,gcc-4.0.0	+50%	+38%	|	+40%	+410%(*)
# Power6,xlc-7		+150%	+90%	|	+100%	+430%(*)
#
# (*)	64-bit code in 32-bit application context, which actually is
#	on TODO list. It should be noted that for safe deployment in
#	32-bit *multi-threaded* context asynchronous signals should be
#	blocked upon entry to SHA512 block routine. This is because
#	32-bit signaling procedure invalidates upper halves of GPRs.
#	Context switch procedure preserves them, but not signaling:-(

# Second version is true multi-thread safe. Trouble with the original
# version was that it was using thread local storage pointer register.
# Well, it scrupulously preserved it, but the problem would arise the
# moment asynchronous signal was delivered and signal handler would
# dereference the TLS pointer. While it's never the case in openssl
# application or test suite, we have to respect this scenario and not
# use TLS pointer register. Alternative would be to require caller to
# block signals prior calling this routine. For the record, in 32-bit
# context R2 serves as TLS pointer, while in 64-bit context - R13.

$flavour=shift;
$output =shift;

if ($flavour =~ /64/) {
	$SIZE_T=8;
	$LRSAVE=2*$SIZE_T;
	$STU="stdu";
	$UCMP="cmpld";
	$SHL="sldi";
	$POP="ld";
	$PUSH="std";
} elsif ($flavour =~ /32/) {
	$SIZE_T=4;
	$LRSAVE=$SIZE_T;
	$STU="stwu";
	$UCMP="cmplw";
	$SHL="slwi";
	$POP="lwz";
	$PUSH="stw";
} else { die "nonsense $flavour"; }

$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0;

$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or
( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or
die "can't locate ppc-xlate.pl";

open STDOUT,"| $^X $xlate $flavour $output" || die "can't call $xlate: $!";

if ($output =~ /512/) {
	$func="sha512_block_ppc";
	$SZ=8;
	@Sigma0=(28,34,39);
	@Sigma1=(14,18,41);
	@sigma0=(1,  8, 7);
	@sigma1=(19,61, 6);
	$rounds=80;
	$LD="ld";
	$ST="std";
	$ROR="rotrdi";
	$SHR="srdi";
} else {
	$func="sha256_block_ppc";
	$SZ=4;
	@Sigma0=( 2,13,22);
	@Sigma1=( 6,11,25);
	@sigma0=( 7,18, 3);
	@sigma1=(17,19,10);
	$rounds=64;
	$LD="lwz";
	$ST="stw";
	$ROR="rotrwi";
	$SHR="srwi";
}

$FRAME=32*$SIZE_T+16*$SZ;
$LOCALS=6*$SIZE_T;

$sp ="r1";
$toc="r2";
$ctx="r3";	# zapped by $a0
$inp="r4";	# zapped by $a1
$num="r5";	# zapped by $t0

$T  ="r0";
$a0 ="r3";
$a1 ="r4";
$t0 ="r5";
$t1 ="r6";
$Tbl="r7";

$A  ="r8";
$B  ="r9";
$C  ="r10";
$D  ="r11";
$E  ="r12";
$F  =$t1;	$t1 = "r0";	# stay away from "r13";
$G  ="r14";
$H  ="r15";

@V=($A,$B,$C,$D,$E,$F,$G,$H);
@X=("r16","r17","r18","r19","r20","r21","r22","r23",
    "r24","r25","r26","r27","r28","r29","r30","r31");

$inp="r31" if($SZ==4 || $SIZE_T==8);	# reassigned $inp! aliases with @X[15]

sub ROUND_00_15 {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
$code.=<<___;
	$ROR	$a0,$e,$Sigma1[0]
	$ROR	$a1,$e,$Sigma1[1]
	and	$t0,$f,$e
	xor	$a0,$a0,$a1
	add	$h,$h,$t1
	andc	$t1,$g,$e
	$ROR	$a1,$a1,`$Sigma1[2]-$Sigma1[1]`
	or	$t0,$t0,$t1		; Ch(e,f,g)
	add	$h,$h,@X[$i%16]
	xor	$a0,$a0,$a1		; Sigma1(e)
	add	$h,$h,$t0
	add	$h,$h,$a0

	$ROR	$a0,$a,$Sigma0[0]
	$ROR	$a1,$a,$Sigma0[1]
	and	$t0,$a,$b
	and	$t1,$a,$c
	xor	$a0,$a0,$a1
	$ROR	$a1,$a1,`$Sigma0[2]-$Sigma0[1]`
	xor	$t0,$t0,$t1
	and	$t1,$b,$c
	xor	$a0,$a0,$a1		; Sigma0(a)
	add	$d,$d,$h
	xor	$t0,$t0,$t1		; Maj(a,b,c)
___
$code.=<<___ if ($i<15);
	$LD	$t1,`($i+1)*$SZ`($Tbl)
___
$code.=<<___;
	add	$h,$h,$a0
	add	$h,$h,$t0

___
}

sub ROUND_16_xx {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
$i-=16;
$code.=<<___;
	$ROR	$a0,@X[($i+1)%16],$sigma0[0]
	$ROR	$a1,@X[($i+1)%16],$sigma0[1]
	$ROR	$t0,@X[($i+14)%16],$sigma1[0]
	$ROR	$t1,@X[($i+14)%16],$sigma1[1]
	xor	$a0,$a0,$a1
	$SHR	$a1,@X[($i+1)%16],$sigma0[2]
	xor	$t0,$t0,$t1
	$SHR	$t1,@X[($i+14)%16],$sigma1[2]
	add	@X[$i],@X[$i],@X[($i+9)%16]
	xor	$a0,$a0,$a1		; sigma0(X[(i+1)&0x0f])
	xor	$t0,$t0,$t1		; sigma1(X[(i+14)&0x0f])
	$LD	$t1,`$i*$SZ`($Tbl)
	add	@X[$i],@X[$i],$a0
	add	@X[$i],@X[$i],$t0
___
&ROUND_00_15($i+16,$a,$b,$c,$d,$e,$f,$g,$h);
}

$code=<<___;
.machine	"any"
.text

.globl	$func
.align	6
$func:
	$STU	$sp,-$FRAME($sp)
	mflr	r0
	$SHL	$num,$num,`log(16*$SZ)/log(2)`

	$PUSH	$ctx,`$FRAME-$SIZE_T*22`($sp)

	$PUSH	r14,`$FRAME-$SIZE_T*18`($sp)
	$PUSH	r15,`$FRAME-$SIZE_T*17`($sp)
	$PUSH	r16,`$FRAME-$SIZE_T*16`($sp)
	$PUSH	r17,`$FRAME-$SIZE_T*15`($sp)
	$PUSH	r18,`$FRAME-$SIZE_T*14`($sp)
	$PUSH	r19,`$FRAME-$SIZE_T*13`($sp)
	$PUSH	r20,`$FRAME-$SIZE_T*12`($sp)
	$PUSH	r21,`$FRAME-$SIZE_T*11`($sp)
	$PUSH	r22,`$FRAME-$SIZE_T*10`($sp)
	$PUSH	r23,`$FRAME-$SIZE_T*9`($sp)
	$PUSH	r24,`$FRAME-$SIZE_T*8`($sp)
	$PUSH	r25,`$FRAME-$SIZE_T*7`($sp)
	$PUSH	r26,`$FRAME-$SIZE_T*6`($sp)
	$PUSH	r27,`$FRAME-$SIZE_T*5`($sp)
	$PUSH	r28,`$FRAME-$SIZE_T*4`($sp)
	$PUSH	r29,`$FRAME-$SIZE_T*3`($sp)
	$PUSH	r30,`$FRAME-$SIZE_T*2`($sp)
	$PUSH	r31,`$FRAME-$SIZE_T*1`($sp)
	$PUSH	r0,`$FRAME+$LRSAVE`($sp)
___

if ($SZ==4 || $SIZE_T==8) {
$code.=<<___;
	$LD	$A,`0*$SZ`($ctx)
	mr	$inp,r4				; incarnate $inp
	$LD	$B,`1*$SZ`($ctx)
	$LD	$C,`2*$SZ`($ctx)
	$LD	$D,`3*$SZ`($ctx)
	$LD	$E,`4*$SZ`($ctx)
	$LD	$F,`5*$SZ`($ctx)
	$LD	$G,`6*$SZ`($ctx)
	$LD	$H,`7*$SZ`($ctx)
___
} else {
  for ($i=16;$i<32;$i++) {
    $code.=<<___;
	lwz	r$i,`$LITTLE_ENDIAN^(4*($i-16))`($ctx)
___
  }
}

$code.=<<___;
	bl	LPICmeup
LPICedup:
	andi.	r0,$inp,3
	bne	Lunaligned
Laligned:
	add	$num,$inp,$num
	$PUSH	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
	bl	Lsha2_block_private
	b	Ldone

; PowerPC specification allows an implementation to be ill-behaved
; upon unaligned access which crosses page boundary. "Better safe
; than sorry" principle makes me treat it specially. But I don't
; look for particular offending word, but rather for the input
; block which crosses the boundary. Once found that block is aligned
; and hashed separately...
.align	4
Lunaligned:
	subfic	$t1,$inp,4096
	andi.	$t1,$t1,`4096-16*$SZ`	; distance to closest page boundary
	beq	Lcross_page
	$UCMP	$num,$t1
	ble	Laligned		; didn't cross the page boundary
	subfc	$num,$t1,$num
	add	$t1,$inp,$t1
	$PUSH	$num,`$FRAME-$SIZE_T*25`($sp)	; save real remaining num
	$PUSH	$t1,`$FRAME-$SIZE_T*24`($sp)	; intermediate end pointer
	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
	bl	Lsha2_block_private
	; $inp equals to the intermediate end pointer here
	$POP	$num,`$FRAME-$SIZE_T*25`($sp)	; restore real remaining num
Lcross_page:
	li	$t1,`16*$SZ/4`
	mtctr	$t1
___
if ($SZ==4 || $SIZE_T==8) {
$code.=<<___;
	addi	r20,$sp,$LOCALS			; aligned spot below the frame
Lmemcpy:
	lbz	r16,0($inp)
	lbz	r17,1($inp)
	lbz	r18,2($inp)
	lbz	r19,3($inp)
	addi	$inp,$inp,4
	stb	r16,0(r20)
	stb	r17,1(r20)
	stb	r18,2(r20)
	stb	r19,3(r20)
	addi	r20,r20,4
	bdnz	Lmemcpy
___
} else {
$code.=<<___;
	addi	r12,$sp,$LOCALS			; aligned spot below the frame
Lmemcpy:
	lbz	r8,0($inp)
	lbz	r9,1($inp)
	lbz	r10,2($inp)
	lbz	r11,3($inp)
	addi	$inp,$inp,4
	stb	r8,0(r12)
	stb	r9,1(r12)
	stb	r10,2(r12)
	stb	r11,3(r12)
	addi	r12,r12,4
	bdnz	Lmemcpy
___
}

$code.=<<___;
	$PUSH	$inp,`$FRAME-$SIZE_T*26`($sp)	; save real inp
	addi	$t1,$sp,`$LOCALS+16*$SZ`	; fictitious end pointer
	addi	$inp,$sp,$LOCALS		; fictitious inp pointer
	$PUSH	$num,`$FRAME-$SIZE_T*25`($sp)	; save real num
	$PUSH	$t1,`$FRAME-$SIZE_T*24`($sp)	; end pointer
	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
	bl	Lsha2_block_private
	$POP	$inp,`$FRAME-$SIZE_T*26`($sp)	; restore real inp
	$POP	$num,`$FRAME-$SIZE_T*25`($sp)	; restore real num
	addic.	$num,$num,`-16*$SZ`		; num--
	bne	Lunaligned

Ldone:
	$POP	r0,`$FRAME+$LRSAVE`($sp)
	$POP	r14,`$FRAME-$SIZE_T*18`($sp)
	$POP	r15,`$FRAME-$SIZE_T*17`($sp)
	$POP	r16,`$FRAME-$SIZE_T*16`($sp)
	$POP	r17,`$FRAME-$SIZE_T*15`($sp)
	$POP	r18,`$FRAME-$SIZE_T*14`($sp)
	$POP	r19,`$FRAME-$SIZE_T*13`($sp)
	$POP	r20,`$FRAME-$SIZE_T*12`($sp)
	$POP	r21,`$FRAME-$SIZE_T*11`($sp)
	$POP	r22,`$FRAME-$SIZE_T*10`($sp)
	$POP	r23,`$FRAME-$SIZE_T*9`($sp)
	$POP	r24,`$FRAME-$SIZE_T*8`($sp)
	$POP	r25,`$FRAME-$SIZE_T*7`($sp)
	$POP	r26,`$FRAME-$SIZE_T*6`($sp)
	$POP	r27,`$FRAME-$SIZE_T*5`($sp)
	$POP	r28,`$FRAME-$SIZE_T*4`($sp)
	$POP	r29,`$FRAME-$SIZE_T*3`($sp)
	$POP	r30,`$FRAME-$SIZE_T*2`($sp)
	$POP	r31,`$FRAME-$SIZE_T*1`($sp)
	mtlr	r0
	addi	$sp,$sp,$FRAME
	blr
	.long	0
	.byte	0,12,4,1,0x80,18,3,0
	.long	0
___

if ($SZ==4 || $SIZE_T==8) {
$code.=<<___;
.align	4
Lsha2_block_private:
	$LD	$t1,0($Tbl)
___
for($i=0;$i<16;$i++) {
$code.=<<___ if ($SZ==4 && !$LITTLE_ENDIAN);
	lwz	@X[$i],`$i*$SZ`($inp)
___
$code.=<<___ if ($SZ==4 && $LITTLE_ENDIAN);
	lwz	$a0,`$i*$SZ`($inp)
	rotlwi	@X[$i],$a0,8
	rlwimi	@X[$i],$a0,24,0,7
	rlwimi	@X[$i],$a0,24,16,23
___
# 64-bit loads are split to 2x32-bit ones, as CPU can't handle
# unaligned 64-bit loads, only 32-bit ones...
$code.=<<___ if ($SZ==8 && !$LITTLE_ENDIAN);
	lwz	$t0,`$i*$SZ`($inp)
	lwz	@X[$i],`$i*$SZ+4`($inp)
	insrdi	@X[$i],$t0,32,0
___
$code.=<<___ if ($SZ==8 && $LITTLE_ENDIAN);
	lwz	$a0,`$i*$SZ`($inp)
	 lwz	$a1,`$i*$SZ+4`($inp)
	rotlwi	$t0,$a0,8
	 rotlwi	@X[$i],$a1,8
	rlwimi	$t0,$a0,24,0,7
	 rlwimi	@X[$i],$a1,24,0,7
	rlwimi	$t0,$a0,24,16,23
	 rlwimi	@X[$i],$a1,24,16,23
	insrdi	@X[$i],$t0,32,0
___
	&ROUND_00_15($i,@V);
	unshift(@V,pop(@V));
}
$code.=<<___;
	li	$t0,`$rounds/16-1`
	mtctr	$t0
.align	4
Lrounds:
	addi	$Tbl,$Tbl,`16*$SZ`
___
for(;$i<32;$i++) {
	&ROUND_16_xx($i,@V);
	unshift(@V,pop(@V));
}
$code.=<<___;
	bdnz	Lrounds

	$POP	$ctx,`$FRAME-$SIZE_T*22`($sp)
	$POP	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
	$POP	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
	subi	$Tbl,$Tbl,`($rounds-16)*$SZ`	; rewind Tbl

	$LD	r16,`0*$SZ`($ctx)
	$LD	r17,`1*$SZ`($ctx)
	$LD	r18,`2*$SZ`($ctx)
	$LD	r19,`3*$SZ`($ctx)
	$LD	r20,`4*$SZ`($ctx)
	$LD	r21,`5*$SZ`($ctx)
	$LD	r22,`6*$SZ`($ctx)
	addi	$inp,$inp,`16*$SZ`		; advance inp
	$LD	r23,`7*$SZ`($ctx)
	add	$A,$A,r16
	add	$B,$B,r17
	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)
	add	$C,$C,r18
	$ST	$A,`0*$SZ`($ctx)
	add	$D,$D,r19
	$ST	$B,`1*$SZ`($ctx)
	add	$E,$E,r20
	$ST	$C,`2*$SZ`($ctx)
	add	$F,$F,r21
	$ST	$D,`3*$SZ`($ctx)
	add	$G,$G,r22
	$ST	$E,`4*$SZ`($ctx)
	add	$H,$H,r23
	$ST	$F,`5*$SZ`($ctx)
	$ST	$G,`6*$SZ`($ctx)
	$UCMP	$inp,$num
	$ST	$H,`7*$SZ`($ctx)
	bne	Lsha2_block_private
	blr
	.long	0
	.byte	0,12,0x14,0,0,0,0,0
.size	$func,.-$func
___
} else {
########################################################################
# SHA512 for PPC32, X vector is off-loaded to stack...
#
#			|	sha512
#			|	-m32
# ----------------------+-----------------------
# PPC74x0,gcc-4.0.1	|	+48%
# POWER6,gcc-4.4.6	|	+124%(*)
# POWER7,gcc-4.4.6	|	+79%(*)
# e300,gcc-4.1.0	|	+167%
#
# (*)	~1/3 of -m64 result [and ~20% better than -m32 code generated
#	by xlc-12.1]

my $XOFF=$LOCALS;

my @V=map("r$_",(16..31));	# A..H

my ($s0,$s1,$t0,$t1,$t2,$t3,$a0,$a1,$a2,$a3)=map("r$_",(0,5,6,8..12,14,15));
my ($x0,$x1)=("r3","r4");	# zaps $ctx and $inp

sub ROUND_00_15_ppc32 {
my ($i,	$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
	$ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo)=@_;

$code.=<<___;
	lwz	$t2,`$SZ*($i%16)+($LITTLE_ENDIAN^4)`($Tbl)
	 xor	$a0,$flo,$glo
	lwz	$t3,`$SZ*($i%16)+($LITTLE_ENDIAN^0)`($Tbl)
	 xor	$a1,$fhi,$ghi
	addc	$hlo,$hlo,$t0			; h+=x[i]
	stw	$t0,`$XOFF+0+$SZ*($i%16)`($sp)	; save x[i]

	srwi	$s0,$elo,$Sigma1[0]
	srwi	$s1,$ehi,$Sigma1[0]
	 and	$a0,$a0,$elo
	adde	$hhi,$hhi,$t1
	 and	$a1,$a1,$ehi
	stw	$t1,`$XOFF+4+$SZ*($i%16)`($sp)
	srwi	$t0,$elo,$Sigma1[1]
	srwi	$t1,$ehi,$Sigma1[1]
	 addc	$hlo,$hlo,$t2			; h+=K512[i]
	insrwi	$s0,$ehi,$Sigma1[0],0
	insrwi	$s1,$elo,$Sigma1[0],0
	 xor	$a0,$a0,$glo			; Ch(e,f,g)
	 adde	$hhi,$hhi,$t3
	 xor	$a1,$a1,$ghi
	insrwi	$t0,$ehi,$Sigma1[1],0
	insrwi	$t1,$elo,$Sigma1[1],0
	 addc	$hlo,$hlo,$a0			; h+=Ch(e,f,g)
	srwi	$t2,$ehi,$Sigma1[2]-32
	srwi	$t3,$elo,$Sigma1[2]-32
	xor	$s0,$s0,$t0
	xor	$s1,$s1,$t1
	insrwi	$t2,$elo,$Sigma1[2]-32,0
	insrwi	$t3,$ehi,$Sigma1[2]-32,0
	 xor	$a0,$alo,$blo			; a^b, b^c in next round
	 adde	$hhi,$hhi,$a1
	 xor	$a1,$ahi,$bhi
	xor	$s0,$s0,$t2			; Sigma1(e)
	xor	$s1,$s1,$t3

	srwi	$t0,$alo,$Sigma0[0]
	 and	$a2,$a2,$a0
	 addc	$hlo,$hlo,$s0			; h+=Sigma1(e)
	 and	$a3,$a3,$a1
	srwi	$t1,$ahi,$Sigma0[0]
	srwi	$s0,$ahi,$Sigma0[1]-32
	 adde	$hhi,$hhi,$s1
	srwi	$s1,$alo,$Sigma0[1]-32
	insrwi	$t0,$ahi,$Sigma0[0],0
	insrwi	$t1,$alo,$Sigma0[0],0
	 xor	$a2,$a2,$blo			; Maj(a,b,c)
	 addc	$dlo,$dlo,$hlo			; d+=h
	 xor	$a3,$a3,$bhi
	insrwi	$s0,$alo,$Sigma0[1]-32,0
	insrwi	$s1,$ahi,$Sigma0[1]-32,0
	 adde	$dhi,$dhi,$hhi
	srwi	$t2,$ahi,$Sigma0[2]-32
	srwi	$t3,$alo,$Sigma0[2]-32
	xor	$s0,$s0,$t0
	 addc	$hlo,$hlo,$a2			; h+=Maj(a,b,c)
	xor	$s1,$s1,$t1
	insrwi	$t2,$alo,$Sigma0[2]-32,0
	insrwi	$t3,$ahi,$Sigma0[2]-32,0
	 adde	$hhi,$hhi,$a3
___
$code.=<<___ if ($i>=15);
	lwz	$t0,`$XOFF+0+$SZ*(($i+2)%16)`($sp)
	lwz	$t1,`$XOFF+4+$SZ*(($i+2)%16)`($sp)
___
$code.=<<___ if ($i<15 && !$LITTLE_ENDIAN);
	lwz	$t1,`$SZ*($i+1)+0`($inp)
	lwz	$t0,`$SZ*($i+1)+4`($inp)
___
$code.=<<___ if ($i<15 && $LITTLE_ENDIAN);
	lwz	$a2,`$SZ*($i+1)+0`($inp)
	 lwz	$a3,`$SZ*($i+1)+4`($inp)
	rotlwi	$t1,$a2,8
	 rotlwi	$t0,$a3,8
	rlwimi	$t1,$a2,24,0,7
	 rlwimi	$t0,$a3,24,0,7
	rlwimi	$t1,$a2,24,16,23
	 rlwimi	$t0,$a3,24,16,23
___
$code.=<<___;
	xor	$s0,$s0,$t2			; Sigma0(a)
	xor	$s1,$s1,$t3
	addc	$hlo,$hlo,$s0			; h+=Sigma0(a)
	adde	$hhi,$hhi,$s1
___
$code.=<<___ if ($i==15);
	lwz	$x0,`$XOFF+0+$SZ*(($i+1)%16)`($sp)
	lwz	$x1,`$XOFF+4+$SZ*(($i+1)%16)`($sp)
___
}
sub ROUND_16_xx_ppc32 {
my ($i,	$ahi,$alo,$bhi,$blo,$chi,$clo,$dhi,$dlo,
	$ehi,$elo,$fhi,$flo,$ghi,$glo,$hhi,$hlo)=@_;

$code.=<<___;
	srwi	$s0,$t0,$sigma0[0]
	srwi	$s1,$t1,$sigma0[0]
	srwi	$t2,$t0,$sigma0[1]
	srwi	$t3,$t1,$sigma0[1]
	insrwi	$s0,$t1,$sigma0[0],0
	insrwi	$s1,$t0,$sigma0[0],0
	srwi	$a0,$t0,$sigma0[2]
	insrwi	$t2,$t1,$sigma0[1],0
	insrwi	$t3,$t0,$sigma0[1],0
	insrwi	$a0,$t1,$sigma0[2],0
	xor	$s0,$s0,$t2
	 lwz	$t2,`$XOFF+0+$SZ*(($i+14)%16)`($sp)
	srwi	$a1,$t1,$sigma0[2]
	xor	$s1,$s1,$t3
	 lwz	$t3,`$XOFF+4+$SZ*(($i+14)%16)`($sp)
	xor	$a0,$a0,$s0
	 srwi	$s0,$t2,$sigma1[0]
	xor	$a1,$a1,$s1
	 srwi	$s1,$t3,$sigma1[0]
	addc	$x0,$x0,$a0			; x[i]+=sigma0(x[i+1])
	 srwi	$a0,$t3,$sigma1[1]-32
	insrwi	$s0,$t3,$sigma1[0],0
	insrwi	$s1,$t2,$sigma1[0],0
	adde	$x1,$x1,$a1
	 srwi	$a1,$t2,$sigma1[1]-32

	insrwi	$a0,$t2,$sigma1[1]-32,0
	srwi	$t2,$t2,$sigma1[2]
	insrwi	$a1,$t3,$sigma1[1]-32,0
	insrwi	$t2,$t3,$sigma1[2],0
	xor	$s0,$s0,$a0
	 lwz	$a0,`$XOFF+0+$SZ*(($i+9)%16)`($sp)
	srwi	$t3,$t3,$sigma1[2]
	xor	$s1,$s1,$a1
	 lwz	$a1,`$XOFF+4+$SZ*(($i+9)%16)`($sp)
	xor	$s0,$s0,$t2
	 addc	$x0,$x0,$a0			; x[i]+=x[i+9]
	xor	$s1,$s1,$t3
	 adde	$x1,$x1,$a1
	addc	$x0,$x0,$s0			; x[i]+=sigma1(x[i+14])
	adde	$x1,$x1,$s1
___
	($t0,$t1,$x0,$x1) = ($x0,$x1,$t0,$t1);
	&ROUND_00_15_ppc32(@_);
}

$code.=<<___;
.align	4
Lsha2_block_private:
___
$code.=<<___ if (!$LITTLE_ENDIAN);
	lwz	$t1,0($inp)
	xor	$a2,@V[3],@V[5]		; B^C, magic seed
	lwz	$t0,4($inp)
	xor	$a3,@V[2],@V[4]
___
$code.=<<___ if ($LITTLE_ENDIAN);
	lwz	$a1,0($inp)
	xor	$a2,@V[3],@V[5]		; B^C, magic seed
	lwz	$a0,4($inp)
	xor	$a3,@V[2],@V[4]
	rotlwi	$t1,$a1,8
	 rotlwi	$t0,$a0,8
	rlwimi	$t1,$a1,24,0,7
	 rlwimi	$t0,$a0,24,0,7
	rlwimi	$t1,$a1,24,16,23
	 rlwimi	$t0,$a0,24,16,23
___
for($i=0;$i<16;$i++) {
	&ROUND_00_15_ppc32($i,@V);
	unshift(@V,pop(@V));	unshift(@V,pop(@V));
	($a0,$a1,$a2,$a3) = ($a2,$a3,$a0,$a1);
}
$code.=<<___;
	li	$a0,`$rounds/16-1`
	mtctr	$a0
.align	4
Lrounds:
	addi	$Tbl,$Tbl,`16*$SZ`
___
for(;$i<32;$i++) {
	&ROUND_16_xx_ppc32($i,@V);
	unshift(@V,pop(@V));	unshift(@V,pop(@V));
	($a0,$a1,$a2,$a3) = ($a2,$a3,$a0,$a1);
}
$code.=<<___;
	bdnz	Lrounds

	$POP	$ctx,`$FRAME-$SIZE_T*22`($sp)
	$POP	$inp,`$FRAME-$SIZE_T*23`($sp)	; inp pointer
	$POP	$num,`$FRAME-$SIZE_T*24`($sp)	; end pointer
	subi	$Tbl,$Tbl,`($rounds-16)*$SZ`	; rewind Tbl

	lwz	$t0,`$LITTLE_ENDIAN^0`($ctx)
	lwz	$t1,`$LITTLE_ENDIAN^4`($ctx)
	lwz	$t2,`$LITTLE_ENDIAN^8`($ctx)
	lwz	$t3,`$LITTLE_ENDIAN^12`($ctx)
	lwz	$a0,`$LITTLE_ENDIAN^16`($ctx)
	lwz	$a1,`$LITTLE_ENDIAN^20`($ctx)
	lwz	$a2,`$LITTLE_ENDIAN^24`($ctx)
	addc	@V[1],@V[1],$t1
	lwz	$a3,`$LITTLE_ENDIAN^28`($ctx)
	adde	@V[0],@V[0],$t0
	lwz	$t0,`$LITTLE_ENDIAN^32`($ctx)
	addc	@V[3],@V[3],$t3
	lwz	$t1,`$LITTLE_ENDIAN^36`($ctx)
	adde	@V[2],@V[2],$t2
	lwz	$t2,`$LITTLE_ENDIAN^40`($ctx)
	addc	@V[5],@V[5],$a1
	lwz	$t3,`$LITTLE_ENDIAN^44`($ctx)
	adde	@V[4],@V[4],$a0
	lwz	$a0,`$LITTLE_ENDIAN^48`($ctx)
	addc	@V[7],@V[7],$a3
	lwz	$a1,`$LITTLE_ENDIAN^52`($ctx)
	adde	@V[6],@V[6],$a2
	lwz	$a2,`$LITTLE_ENDIAN^56`($ctx)
	addc	@V[9],@V[9],$t1
	lwz	$a3,`$LITTLE_ENDIAN^60`($ctx)
	adde	@V[8],@V[8],$t0
	stw	@V[0],`$LITTLE_ENDIAN^0`($ctx)
	stw	@V[1],`$LITTLE_ENDIAN^4`($ctx)
	addc	@V[11],@V[11],$t3
	stw	@V[2],`$LITTLE_ENDIAN^8`($ctx)
	stw	@V[3],`$LITTLE_ENDIAN^12`($ctx)
	adde	@V[10],@V[10],$t2
	stw	@V[4],`$LITTLE_ENDIAN^16`($ctx)
	stw	@V[5],`$LITTLE_ENDIAN^20`($ctx)
	addc	@V[13],@V[13],$a1
	stw	@V[6],`$LITTLE_ENDIAN^24`($ctx)
	stw	@V[7],`$LITTLE_ENDIAN^28`($ctx)
	adde	@V[12],@V[12],$a0
	stw	@V[8],`$LITTLE_ENDIAN^32`($ctx)
	stw	@V[9],`$LITTLE_ENDIAN^36`($ctx)
	addc	@V[15],@V[15],$a3
	stw	@V[10],`$LITTLE_ENDIAN^40`($ctx)
	stw	@V[11],`$LITTLE_ENDIAN^44`($ctx)
	adde	@V[14],@V[14],$a2
	stw	@V[12],`$LITTLE_ENDIAN^48`($ctx)
	stw	@V[13],`$LITTLE_ENDIAN^52`($ctx)
	stw	@V[14],`$LITTLE_ENDIAN^56`($ctx)
	stw	@V[15],`$LITTLE_ENDIAN^60`($ctx)

	addi	$inp,$inp,`16*$SZ`		; advance inp
	$PUSH	$inp,`$FRAME-$SIZE_T*23`($sp)
	$UCMP	$inp,$num
	bne	Lsha2_block_private
	blr
	.long	0
	.byte	0,12,0x14,0,0,0,0,0
.size	$func,.-$func
___
}

# Ugly hack here, because PPC assembler syntax seem to vary too
# much from platforms to platform...
$code.=<<___;
.align	6
LPICmeup:
	mflr	r0
	bcl	20,31,\$+4
	mflr	$Tbl	; vvvvvv "distance" between . and 1st data entry
	addi	$Tbl,$Tbl,`64-8`
	mtlr	r0
	blr
	.long	0
	.byte	0,12,0x14,0,0,0,0,0
	.space	`64-9*4`
___
$code.=<<___ if ($SZ==8);
	.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.=<<___ if ($SZ==4);
	.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
___

$code =~ s/\`([^\`]*)\`/eval $1/gem;
print $code;
close STDOUT or die "error closing STDOUT: $!";


#! /usr/bin/env perl
# Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

# ====================================================================
# Written by Andy Polyakov <appro@openssl.org> 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/.
# ====================================================================

# SHA256/512 block procedures for s390x.

# April 2007.
#
# sha256_block_data_order is reportedly >3 times faster than gcc 3.3
# generated code (must be a bug in compiler, as improvement is
# "pathologically" high, in particular in comparison to other SHA
# modules). But the real twist is that it detects if hardware support
# for SHA256 is available and in such case utilizes it. Then the
# performance can reach >6.5x of assembler one for larger chunks.
#
# sha512_block_data_order is ~70% faster than gcc 3.3 generated code.

# January 2009.
#
# Add support for hardware SHA512 and reschedule instructions to
# favour dual-issue z10 pipeline. Hardware SHA256/512 is ~4.7x faster
# than software.

# November 2010.
#
# Adapt for -m31 build. If kernel supports what's called "highgprs"
# feature on Linux [see /proc/cpuinfo], it's possible to use 64-bit
# instructions and achieve "64-bit" performance even in 31-bit legacy
# application context. The feature is not specific to any particular
# processor, as long as it's "z-CPU". Latter implies that the code
# remains z/Architecture specific. On z990 SHA256 was measured to
# perform 2.4x and SHA512 - 13x better than code generated by gcc 4.3.

$flavour = shift;

if ($flavour =~ /3[12]/) {
	$SIZE_T=4;
	$g="";
} else {
	$SIZE_T=8;
	$g="g";
}

$t0="%r0";
$t1="%r1";
$ctx="%r2";	$t2="%r2";
$inp="%r3";
$len="%r4";	# used as index in inner loop

$A="%r5";
$B="%r6";
$C="%r7";
$D="%r8";
$E="%r9";
$F="%r10";
$G="%r11";
$H="%r12";	@V=($A,$B,$C,$D,$E,$F,$G,$H);
$tbl="%r13";
$T1="%r14";
$sp="%r15";

while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
open STDOUT,">$output";

if ($output =~ /512/) {
	$label="512";
	$SZ=8;
	$LD="lg";	# load from memory
	$ST="stg";	# store to memory
	$ADD="alg";	# add with memory operand
	$ROT="rllg";	# rotate left
	$SHR="srlg";	# logical right shift [see even at the end]
	@Sigma0=(25,30,36);
	@Sigma1=(23,46,50);
	@sigma0=(56,63, 7);
	@sigma1=( 3,45, 6);
	$rounds=80;
	$kimdfunc=3;	# 0 means unknown/unsupported/unimplemented/disabled
} else {
	$label="256";
	$SZ=4;
	$LD="llgf";	# load from memory
	$ST="st";	# store to memory
	$ADD="al";	# add with memory operand
	$ROT="rll";	# rotate left
	$SHR="srl";	# logical right shift
	@Sigma0=(10,19,30);
	@Sigma1=( 7,21,26);
	@sigma0=(14,25, 3);
	@sigma1=(13,15,10);
	$rounds=64;
	$kimdfunc=2;	# magic function code for kimd instruction
}
$Func="sha${label}_block_data_order";
$Table="K${label}";
$stdframe=16*$SIZE_T+4*8;
$frame=$stdframe+16*$SZ;

sub BODY_00_15 {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;

$code.=<<___ if ($i<16);
	$LD	$T1,`$i*$SZ`($inp)	### $i
___
$code.=<<___;
	$ROT	$t0,$e,$Sigma1[0]
	$ROT	$t1,$e,$Sigma1[1]
	 lgr	$t2,$f
	xgr	$t0,$t1
	$ROT	$t1,$t1,`$Sigma1[2]-$Sigma1[1]`
	 xgr	$t2,$g
	$ST	$T1,`$stdframe+$SZ*($i%16)`($sp)
	xgr	$t0,$t1			# Sigma1(e)
	algr	$T1,$h			# T1+=h
	 ngr	$t2,$e
	 lgr	$t1,$a
	algr	$T1,$t0			# T1+=Sigma1(e)
	$ROT	$h,$a,$Sigma0[0]
	 xgr	$t2,$g			# Ch(e,f,g)
	$ADD	$T1,`$i*$SZ`($len,$tbl)	# T1+=K[i]
	$ROT	$t0,$a,$Sigma0[1]
	algr	$T1,$t2			# T1+=Ch(e,f,g)
	 ogr	$t1,$b
	xgr	$h,$t0
	 lgr	$t2,$a
	 ngr	$t1,$c
	$ROT	$t0,$t0,`$Sigma0[2]-$Sigma0[1]`
	xgr	$h,$t0			# h=Sigma0(a)
	 ngr	$t2,$b
	algr	$h,$T1			# h+=T1
	 ogr	$t2,$t1			# Maj(a,b,c)
	algr	$d,$T1			# d+=T1
	algr	$h,$t2			# h+=Maj(a,b,c)
___
}

sub BODY_16_XX {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_;

$code.=<<___;
	$LD	$T1,`$stdframe+$SZ*(($i+1)%16)`($sp)	### $i
	$LD	$t1,`$stdframe+$SZ*(($i+14)%16)`($sp)
	$ROT	$t0,$T1,$sigma0[0]
	$SHR	$T1,$sigma0[2]
	$ROT	$t2,$t0,`$sigma0[1]-$sigma0[0]`
	xgr	$T1,$t0
	$ROT	$t0,$t1,$sigma1[0]
	xgr	$T1,$t2					# sigma0(X[i+1])
	$SHR	$t1,$sigma1[2]
	$ADD	$T1,`$stdframe+$SZ*($i%16)`($sp)	# +=X[i]
	xgr	$t1,$t0
	$ROT	$t0,$t0,`$sigma1[1]-$sigma1[0]`
	$ADD	$T1,`$stdframe+$SZ*(($i+9)%16)`($sp)	# +=X[i+9]
	xgr	$t1,$t0				# sigma1(X[i+14])
	algr	$T1,$t1				# +=sigma1(X[i+14])
___
	&BODY_00_15(@_);
}

$code.=<<___;
#include "s390x_arch.h"

.text
.align	64
.type	$Table,\@object
$Table:
___
$code.=<<___ if ($SZ==4);
	.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
___
$code.=<<___ if ($SZ==8);
	.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.=<<___;
.size	$Table,.-$Table
.globl	$Func
.type	$Func,\@function
$Func:
	sllg	$len,$len,`log(16*$SZ)/log(2)`
___
$code.=<<___ if ($kimdfunc);
	larl	%r1,OPENSSL_s390xcap_P
	lg	%r0,S390X_KIMD(%r1)	# check kimd capabilities
	tmhh	%r0,`0x8000>>$kimdfunc`
	jz	.Lsoftware
	lghi	%r0,$kimdfunc
	lgr	%r1,$ctx
	lgr	%r2,$inp
	lgr	%r3,$len
	.long	0xb93e0002	# kimd %r0,%r2
	brc	1,.-4		# pay attention to "partial completion"
	br	%r14
.align	16
.Lsoftware:
___
$code.=<<___;
	lghi	%r1,-$frame
	la	$len,0($len,$inp)
	stm${g}	$ctx,%r15,`2*$SIZE_T`($sp)
	lgr	%r0,$sp
	la	$sp,0(%r1,$sp)
	st${g}	%r0,0($sp)

	larl	$tbl,$Table
	$LD	$A,`0*$SZ`($ctx)
	$LD	$B,`1*$SZ`($ctx)
	$LD	$C,`2*$SZ`($ctx)
	$LD	$D,`3*$SZ`($ctx)
	$LD	$E,`4*$SZ`($ctx)
	$LD	$F,`5*$SZ`($ctx)
	$LD	$G,`6*$SZ`($ctx)
	$LD	$H,`7*$SZ`($ctx)

.Lloop:
	lghi	$len,0
___
for ($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
$code.=".Lrounds_16_xx:\n";
for (;$i<32;$i++)	{ &BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
$code.=<<___;
	aghi	$len,`16*$SZ`
	lghi	$t0,`($rounds-16)*$SZ`
	clgr	$len,$t0
	jne	.Lrounds_16_xx

	l${g}	$ctx,`$frame+2*$SIZE_T`($sp)
	la	$inp,`16*$SZ`($inp)
	$ADD	$A,`0*$SZ`($ctx)
	$ADD	$B,`1*$SZ`($ctx)
	$ADD	$C,`2*$SZ`($ctx)
	$ADD	$D,`3*$SZ`($ctx)
	$ADD	$E,`4*$SZ`($ctx)
	$ADD	$F,`5*$SZ`($ctx)
	$ADD	$G,`6*$SZ`($ctx)
	$ADD	$H,`7*$SZ`($ctx)
	$ST	$A,`0*$SZ`($ctx)
	$ST	$B,`1*$SZ`($ctx)
	$ST	$C,`2*$SZ`($ctx)
	$ST	$D,`3*$SZ`($ctx)
	$ST	$E,`4*$SZ`($ctx)
	$ST	$F,`5*$SZ`($ctx)
	$ST	$G,`6*$SZ`($ctx)
	$ST	$H,`7*$SZ`($ctx)
	cl${g}	$inp,`$frame+4*$SIZE_T`($sp)
	jne	.Lloop

	lm${g}	%r6,%r15,`$frame+6*$SIZE_T`($sp)
	br	%r14
.size	$Func,.-$Func
.string	"SHA${label} block transform for s390x, CRYPTOGAMS by <appro\@openssl.org>"
___

$code =~ s/\`([^\`]*)\`/eval $1/gem;
# unlike 32-bit shift 64-bit one takes three arguments
$code =~ s/(srlg\s+)(%r[0-9]+),/$1$2,$2,/gm;

print $code;
close STDOUT or die "error closing STDOUT: $!";


#! /usr/bin/env perl
# Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the OpenSSL license (the "License").  You may not use
# this file except in compliance with the License.  You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

# ====================================================================
# Written by Andy Polyakov <appro@openssl.org> 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/.
#
# Hardware SPARC T4 support by David S. Miller
# ====================================================================

# SHA256 performance improvement over compiler generated code varies
# from 40% for Sun C [32-bit build] to 70% for gcc [3.3, 64-bit
# build]. Just like in SHA1 module I aim to ensure scalability on
# UltraSPARC T1 by packing X[16] to 8 64-bit registers.

# SHA512 on pre-T1 UltraSPARC.
#
# Performance is >75% better than 64-bit code generated by Sun C and
# over 2x than 32-bit code. X[16] resides on stack, but access to it
# is scheduled for L2 latency and staged through 32 least significant
# bits of %l0-%l7. The latter is done to achieve 32-/64-bit ABI
# duality. Nevertheless it's ~40% faster than SHA256, which is pretty
# good [optimal coefficient is 50%].
#
# SHA512 on UltraSPARC T1.
#
# It's not any faster than 64-bit code generated by Sun C 5.8. This is
# because 64-bit code generator has the advantage of using 64-bit
# loads(*) to access X[16], which I consciously traded for 32-/64-bit
# ABI duality [as per above]. But it surpasses 32-bit Sun C generated
# code by 60%, not to mention that it doesn't suffer from severe decay
# when running 4 times physical cores threads and that it leaves gcc
# [3.4] behind by over 4x factor! If compared to SHA256, single thread
# performance is only 10% better, but overall throughput for maximum
# amount of threads for given CPU exceeds corresponding one of SHA256
# by 30% [again, optimal coefficient is 50%].
#
# (*)	Unlike pre-T1 UltraSPARC loads on T1 are executed strictly
#	in-order, i.e. load instruction has to complete prior next
#	instruction in given thread is executed, even if the latter is
#	not dependent on load result! This means that on T1 two 32-bit
#	loads are always slower than one 64-bit load. Once again this
#	is unlike pre-T1 UltraSPARC, where, if scheduled appropriately,
#	2x32-bit loads can be as fast as 1x64-bit ones.
#
# SPARC T4 SHA256/512 hardware achieves 3.17/2.01 cycles per byte,
# which is 9.3x/11.1x faster than software. Multi-process benchmark
# saturates at 11.5x single-process result on 8-core processor, or
# ~11/16GBps per 2.85GHz socket.

$output=pop;
open STDOUT,">$output";

if ($output =~ /512/) {
	$label="512";
	$SZ=8;
	$LD="ldx";		# load from memory
	$ST="stx";		# store to memory
	$SLL="sllx";		# shift left logical
	$SRL="srlx";		# shift right logical
	@Sigma0=(28,34,39);
	@Sigma1=(14,18,41);
	@sigma0=( 7, 1, 8);	# right shift first
	@sigma1=( 6,19,61);	# right shift first
	$lastK=0x817;
	$rounds=80;
	$align=4;

	$locals=16*$SZ;		# X[16]

	$A="%o0";
	$B="%o1";
	$C="%o2";
	$D="%o3";
	$E="%o4";
	$F="%o5";
	$G="%g1";
	$H="%o7";
	@V=($A,$B,$C,$D,$E,$F,$G,$H);
} else {
	$label="256";
	$SZ=4;
	$LD="ld";		# load from memory
	$ST="st";		# store to memory
	$SLL="sll";		# shift left logical
	$SRL="srl";		# shift right logical
	@Sigma0=( 2,13,22);
	@Sigma1=( 6,11,25);
	@sigma0=( 3, 7,18);	# right shift first
	@sigma1=(10,17,19);	# right shift first
	$lastK=0x8f2;
	$rounds=64;
	$align=8;

	$locals=0;		# X[16] is register resident
	@X=("%o0","%o1","%o2","%o3","%o4","%o5","%g1","%o7");

	$A="%l0";
	$B="%l1";
	$C="%l2";
	$D="%l3";
	$E="%l4";
	$F="%l5";
	$G="%l6";
	$H="%l7";
	@V=($A,$B,$C,$D,$E,$F,$G,$H);
}
$T1="%g2";
$tmp0="%g3";
$tmp1="%g4";
$tmp2="%g5";

$ctx="%i0";
$inp="%i1";
$len="%i2";
$Ktbl="%i3";
$tmp31="%i4";
$tmp32="%i5";

########### SHA256
$Xload = sub {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;

    if ($i==0) {
$code.=<<___;
	ldx	[$inp+0],@X[0]
	ldx	[$inp+16],@X[2]
	ldx	[$inp+32],@X[4]
	ldx	[$inp+48],@X[6]
	ldx	[$inp+8],@X[1]
	ldx	[$inp+24],@X[3]
	subcc	%g0,$tmp31,$tmp32 ! should be 64-$tmp31, but -$tmp31 works too
	ldx	[$inp+40],@X[5]
	bz,pt	%icc,.Laligned
	ldx	[$inp+56],@X[7]

	sllx	@X[0],$tmp31,@X[0]
	ldx	[$inp+64],$T1
___
for($j=0;$j<7;$j++)
{   $code.=<<___;
	srlx	@X[$j+1],$tmp32,$tmp1
	sllx	@X[$j+1],$tmp31,@X[$j+1]
	or	$tmp1,@X[$j],@X[$j]
___
}
$code.=<<___;
	srlx	$T1,$tmp32,$T1
	or	$T1,@X[7],@X[7]
.Laligned:
___
    }

    if ($i&1) {
	$code.="\tadd	@X[$i/2],$h,$T1\n";
    } else {
	$code.="\tsrlx	@X[$i/2],32,$T1\n\tadd	$h,$T1,$T1\n";
    }
} if ($SZ==4);

########### SHA512
$Xload = sub {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;
my @pair=("%l".eval(($i*2)%8),"%l".eval(($i*2)%8+1),"%l".eval((($i+1)*2)%8));

$code.=<<___ if ($i==0);
	ld	[$inp+0],%l0
	ld	[$inp+4],%l1
	ld	[$inp+8],%l2
	ld	[$inp+12],%l3
	ld	[$inp+16],%l4
	ld	[$inp+20],%l5
	ld	[$inp+24],%l6
	cmp	$tmp31,0
	ld	[$inp+28],%l7
___
$code.=<<___ if ($i<15);
	sllx	@pair[1],$tmp31,$tmp2	! Xload($i)
	add	$tmp31,32,$tmp0
	sllx	@pair[0],$tmp0,$tmp1
	`"ld	[$inp+".eval(32+0+$i*8)."],@pair[0]"	if ($i<12)`
	srlx	@pair[2],$tmp32,@pair[1]
	or	$tmp1,$tmp2,$tmp2
	or	@pair[1],$tmp2,$tmp2
	`"ld	[$inp+".eval(32+4+$i*8)."],@pair[1]"	if ($i<12)`
	add	$h,$tmp2,$T1
	$ST	$tmp2,[%sp+STACK_BIAS+STACK_FRAME+`$i*$SZ`]
___
$code.=<<___ if ($i==12);
	bnz,a,pn	%icc,.+8
	ld	[$inp+128],%l0
___
$code.=<<___ if ($i==15);
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+0`],%l2
	sllx	@pair[1],$tmp31,$tmp2	! Xload($i)
	add	$tmp31,32,$tmp0
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+4`],%l3
	sllx	@pair[0],$tmp0,$tmp1
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+0`],%l4
	srlx	@pair[2],$tmp32,@pair[1]
	or	$tmp1,$tmp2,$tmp2
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+4`],%l5
	or	@pair[1],$tmp2,$tmp2
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+0`],%l6
	add	$h,$tmp2,$T1
	$ST	$tmp2,[%sp+STACK_BIAS+STACK_FRAME+`$i*$SZ`]
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+4`],%l7
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+0`],%l0
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+4`],%l1
___
} if ($SZ==8);

########### common
sub BODY_00_15 {
my ($i,$a,$b,$c,$d,$e,$f,$g,$h)=@_;

    if ($i<16) {
	&$Xload(@_);
    } else {
	$code.="\tadd	$h,$T1,$T1\n";
    }

$code.=<<___;
	$SRL	$e,@Sigma1[0],$h	!! $i
	xor	$f,$g,$tmp2
	$SLL	$e,`$SZ*8-@Sigma1[2]`,$tmp1
	and	$e,$tmp2,$tmp2
	$SRL	$e,@Sigma1[1],$tmp0
	xor	$tmp1,$h,$h
	$SLL	$e,`$SZ*8-@Sigma1[1]`,$tmp1
	xor	$tmp0,$h,$h
	$SRL	$e,@Sigma1[2],$tmp0
	xor	$tmp1,$h,$h
	$SLL	$e,`$SZ*8-@Sigma1[0]`,$tmp1
	xor	$tmp0,$h,$h
	xor	$g,$tmp2,$tmp2		! Ch(e,f,g)
	xor	$tmp1,$h,$tmp0		! Sigma1(e)

	$SRL	$a,@Sigma0[0],$h
	add	$tmp2,$T1,$T1
	$LD	[$Ktbl+`$i*$SZ`],$tmp2	! K[$i]
	$SLL	$a,`$SZ*8-@Sigma0[2]`,$tmp1
	add	$tmp0,$T1,$T1
	$SRL	$a,@Sigma0[1],$tmp0
	xor	$tmp1,$h,$h
	$SLL	$a,`$SZ*8-@Sigma0[1]`,$tmp1
	xor	$tmp0,$h,$h
	$SRL	$a,@Sigma0[2],$tmp0
	xor	$tmp1,$h,$h
	$SLL	$a,`$SZ*8-@Sigma0[0]`,$tmp1
	xor	$tmp0,$h,$h
	xor	$tmp1,$h,$h		! Sigma0(a)

	or	$a,$b,$tmp0
	and	$a,$b,$tmp1
	and	$c,$tmp0,$tmp0
	or	$tmp0,$tmp1,$tmp1	! Maj(a,b,c)
	add	$tmp2,$T1,$T1		! +=K[$i]
	add	$tmp1,$h,$h

	add	$T1,$d,$d
	add	$T1,$h,$h
___
}

########### SHA256
$BODY_16_XX = sub {
my $i=@_[0];
my $xi;

    if ($i&1) {
	$xi=$tmp32;
	$code.="\tsrlx	@X[(($i+1)/2)%8],32,$xi\n";
    } else {
	$xi=@X[(($i+1)/2)%8];
    }
$code.=<<___;
	srl	$xi,@sigma0[0],$T1		!! Xupdate($i)
	sll	$xi,`32-@sigma0[2]`,$tmp1
	srl	$xi,@sigma0[1],$tmp0
	xor	$tmp1,$T1,$T1
	sll	$tmp1,`@sigma0[2]-@sigma0[1]`,$tmp1
	xor	$tmp0,$T1,$T1
	srl	$xi,@sigma0[2],$tmp0
	xor	$tmp1,$T1,$T1
___
    if ($i&1) {
	$xi=@X[(($i+14)/2)%8];
    } else {
	$xi=$tmp32;
	$code.="\tsrlx	@X[(($i+14)/2)%8],32,$xi\n";
    }
$code.=<<___;
	srl	$xi,@sigma1[0],$tmp2
	xor	$tmp0,$T1,$T1			! T1=sigma0(X[i+1])
	sll	$xi,`32-@sigma1[2]`,$tmp1
	srl	$xi,@sigma1[1],$tmp0
	xor	$tmp1,$tmp2,$tmp2
	sll	$tmp1,`@sigma1[2]-@sigma1[1]`,$tmp1
	xor	$tmp0,$tmp2,$tmp2
	srl	$xi,@sigma1[2],$tmp0
	xor	$tmp1,$tmp2,$tmp2
___
    if ($i&1) {
	$xi=@X[($i/2)%8];
$code.=<<___;
	srlx	@X[(($i+9)/2)%8],32,$tmp1	! X[i+9]
	xor	$tmp0,$tmp2,$tmp2		! sigma1(X[i+14])
	srl	@X[($i/2)%8],0,$tmp0
	add	$tmp2,$tmp1,$tmp1
	add	$xi,$T1,$T1			! +=X[i]
	xor	$tmp0,@X[($i/2)%8],@X[($i/2)%8]
	add	$tmp1,$T1,$T1

	srl	$T1,0,$T1
	or	$T1,@X[($i/2)%8],@X[($i/2)%8]
___
    } else {
	$xi=@X[(($i+9)/2)%8];
$code.=<<___;
	srlx	@X[($i/2)%8],32,$tmp1		! X[i]
	xor	$tmp0,$tmp2,$tmp2		! sigma1(X[i+14])
	add	$xi,$T1,$T1			! +=X[i+9]
	add	$tmp2,$tmp1,$tmp1
	srl	@X[($i/2)%8],0,@X[($i/2)%8]
	add	$tmp1,$T1,$T1

	sllx	$T1,32,$tmp0
	or	$tmp0,@X[($i/2)%8],@X[($i/2)%8]
___
    }
    &BODY_00_15(@_);
} if ($SZ==4);

########### SHA512
$BODY_16_XX = sub {
my $i=@_[0];
my @pair=("%l".eval(($i*2)%8),"%l".eval(($i*2)%8+1));

$code.=<<___;
	sllx	%l2,32,$tmp0		!! Xupdate($i)
	or	%l3,$tmp0,$tmp0

	srlx	$tmp0,@sigma0[0],$T1
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+0`],%l2
	sllx	$tmp0,`64-@sigma0[2]`,$tmp1
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+1)%16)*$SZ+4`],%l3
	srlx	$tmp0,@sigma0[1],$tmp0
	xor	$tmp1,$T1,$T1
	sllx	$tmp1,`@sigma0[2]-@sigma0[1]`,$tmp1
	xor	$tmp0,$T1,$T1
	srlx	$tmp0,`@sigma0[2]-@sigma0[1]`,$tmp0
	xor	$tmp1,$T1,$T1
	sllx	%l6,32,$tmp2
	xor	$tmp0,$T1,$T1		! sigma0(X[$i+1])
	or	%l7,$tmp2,$tmp2

	srlx	$tmp2,@sigma1[0],$tmp1
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+0`],%l6
	sllx	$tmp2,`64-@sigma1[2]`,$tmp0
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+14)%16)*$SZ+4`],%l7
	srlx	$tmp2,@sigma1[1],$tmp2
	xor	$tmp0,$tmp1,$tmp1
	sllx	$tmp0,`@sigma1[2]-@sigma1[1]`,$tmp0
	xor	$tmp2,$tmp1,$tmp1
	srlx	$tmp2,`@sigma1[2]-@sigma1[1]`,$tmp2
	xor	$tmp0,$tmp1,$tmp1
	sllx	%l4,32,$tmp0
	xor	$tmp2,$tmp1,$tmp1	! sigma1(X[$i+14])
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+0`],%l4
	or	%l5,$tmp0,$tmp0
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+9)%16)*$SZ+4`],%l5

	sllx	%l0,32,$tmp2
	add	$tmp1,$T1,$T1
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+0`],%l0
	or	%l1,$tmp2,$tmp2
	add	$tmp0,$T1,$T1		! +=X[$i+9]
	ld	[%sp+STACK_BIAS+STACK_FRAME+`(($i+1+0)%16)*$SZ+4`],%l1
	add	$tmp2,$T1,$T1		! +=X[$i]
	$ST	$T1,[%sp+STACK_BIAS+STACK_FRAME+`($i%16)*$SZ`]
___
    &BODY_00_15(@_);
} if ($SZ==8);

$code.=<<___;
#include "sparc_arch.h"

#ifdef __arch64__
.register	%g2,#scratch
.register	%g3,#scratch
#endif

.section	".text",#alloc,#execinstr

.align	64
K${label}:
.type	K${label},#object
___
if ($SZ==4) {
$code.=<<___;
	.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.=<<___;
	.long	0x428a2f98,0xd728ae22, 0x71374491,0x23ef65cd
	.long	0xb5c0fbcf,0xec4d3b2f, 0xe9b5dba5,0x8189dbbc
	.long	0x3956c25b,0xf348b538, 0x59f111f1,0xb605d019
	.long	0x923f82a4,0xaf194f9b, 0xab1c5ed5,0xda6d8118
	.long	0xd807aa98,0xa3030242, 0x12835b01,0x45706fbe
	.long	0x243185be,0x4ee4b28c, 0x550c7dc3,0xd5ffb4e2
	.long	0x72be5d74,0xf27b896f, 0x80deb1fe,0x3b1696b1
	.long	0x9bdc06a7,0x25c71235, 0xc19bf174,0xcf692694
	.long	0xe49b69c1,0x9ef14ad2, 0xefbe4786,0x384f25e3
	.long	0x0fc19dc6,0x8b8cd5b5, 0x240ca1cc,0x77ac9c65
	.long	0x2de92c6f,0x592b0275, 0x4a7484aa,0x6ea6e483
	.long	0x5cb0a9dc,0xbd41fbd4, 0x76f988da,0x831153b5
	.long	0x983e5152,0xee66dfab, 0xa831c66d,0x2db43210
	.long	0xb00327c8,0x98fb213f, 0xbf597fc7,0xbeef0ee4
	.long	0xc6e00bf3,0x3da88fc2, 0xd5a79147,0x930aa725
	.long	0x06ca6351,0xe003826f, 0x14292967,0x0a0e6e70
	.long	0x27b70a85,0x46d22ffc, 0x2e1b2138,0x5c26c926
	.long	0x4d2c6dfc,0x5ac42aed, 0x53380d13,0x9d95b3df
	.long	0x650a7354,0x8baf63de, 0x766a0abb,0x3c77b2a8
	.long	0x81c2c92e,0x47edaee6, 0x92722c85,0x1482353b
	.long	0xa2bfe8a1,0x4cf10364, 0xa81a664b,0xbc423001
	.long	0xc24b8b70,0xd0f89791, 0xc76c51a3,0x0654be30
	.long	0xd192e819,0xd6ef5218, 0xd6990624,0x5565a910
	.long	0xf40e3585,0x5771202a, 0x106aa070,0x32bbd1b8
	.long	0x19a4c116,0xb8d2d0c8, 0x1e376c08,0x5141ab53
	.long	0x2748774c,0xdf8eeb99, 0x34b0bcb5,0xe19b48a8
	.long	0x391c0cb3,0xc5c95a63, 0x4ed8aa4a,0xe3418acb
	.long	0x5b9cca4f,0x7763e373, 0x682e6ff3,0xd6b2b8a3
	.long	0x748f82ee,0x5defb2fc, 0x78a5636f,0x43172f60
	.long	0x84c87814,0xa1f0ab72, 0x8cc70208,0x1a6439ec
	.long	0x90befffa,0x23631e28, 0xa4506ceb,0xde82bde9
	.long	0xbef9a3f7,0xb2c67915, 0xc67178f2,0xe372532b
	.long	0xca273ece,0xea26619c, 0xd186b8c7,0x21c0c207
	.long	0xeada7dd6,0xcde0eb1e, 0xf57d4f7f,0xee6ed178
	.long	0x06f067aa,0x72176fba, 0x0a637dc5,0xa2c898a6
	.long	0x113f9804,0xbef90dae, 0x1b710b35,0x131c471b
	.long	0x28db77f5,0x23047d84, 0x32caab7b,0x40c72493
	.long	0x3c9ebe0a,0x15c9bebc, 0x431d67c4,0x9c100d4c
	.long	0x4cc5d4be,0xcb3e42b6, 0x597f299c,0xfc657e2a
	.long	0x5fcb6fab,0x3ad6faec, 0x6c44198c,0x4a475817
___
}
$code.=<<___;
.size	K${label},.-K${label}

#ifdef __PIC__
SPARC_PIC_THUNK(%g1)
#endif

.globl	sha${label}_block_data_order
.align	32
sha${label}_block_data_order:
	SPARC_LOAD_ADDRESS_LEAF(OPENSSL_sparcv9cap_P,%g1,%g5)
	ld	[%g1+4],%g1		! OPENSSL_sparcv9cap_P[1]

	andcc	%g1, CFR_SHA${label}, %g0
	be	.Lsoftware
	nop
___
$code.=<<___ if ($SZ==8); 		# SHA512
	ldd	[%o0 + 0x00], %f0	! load context
	ldd	[%o0 + 0x08], %f2
	ldd	[%o0 + 0x10], %f4
	ldd	[%o0 + 0x18], %f6
	ldd	[%o0 + 0x20], %f8
	ldd	[%o0 + 0x28], %f10
	andcc	%o1, 0x7, %g0
	ldd	[%o0 + 0x30], %f12
	bne,pn	%icc, .Lhwunaligned
	 ldd	[%o0 + 0x38], %f14

.Lhwaligned_loop:
	ldd	[%o1 + 0x00], %f16
	ldd	[%o1 + 0x08], %f18
	ldd	[%o1 + 0x10], %f20
	ldd	[%o1 + 0x18], %f22
	ldd	[%o1 + 0x20], %f24
	ldd	[%o1 + 0x28], %f26
	ldd	[%o1 + 0x30], %f28
	ldd	[%o1 + 0x38], %f30
	ldd	[%o1 + 0x40], %f32
	ldd	[%o1 + 0x48], %f34
	ldd	[%o1 + 0x50], %f36
	ldd	[%o1 + 0x58], %f38
	ldd	[%o1 + 0x60], %f40
	ldd	[%o1 + 0x68], %f42
	ldd	[%o1 + 0x70], %f44
	subcc	%o2, 1, %o2		! done yet?
	ldd	[%o1 + 0x78], %f46
	add	%o1, 0x80, %o1
	prefetch [%o1 + 63], 20
	prefetch [%o1 + 64+63], 20

	.word	0x81b02860		! SHA512

	bne,pt	SIZE_T_CC, .Lhwaligned_loop
	nop

.Lhwfinish:
	std	%f0, [%o0 + 0x00]	! store context
	std	%f2, [%o0 + 0x08]
	std	%f4, [%o0 + 0x10]
	std	%f6, [%o0 + 0x18]
	std	%f8, [%o0 + 0x20]
	std	%f10, [%o0 + 0x28]
	std	%f12, [%o0 + 0x30]
	retl
	 std	%f14, [%o0 + 0x38]

.align	16
.Lhwunaligned:
	alignaddr %o1, %g0, %o1

	ldd	[%o1 + 0x00], %f18
.Lhwunaligned_loop:
	ldd	[%o1 + 0x08], %f20
	ldd	[%o1 + 0x10], %f22
	ldd	[%o1 + 0x18], %f24
	ldd	[%o1 + 0x20], %f26
	ldd	[%o1 + 0x28], %f28
	ldd	[%o1 + 0x30], %f30
	ldd	[%o1 + 0x38], %f32
	ldd	[%o1 + 0x40], %f34
	ldd	[%o1 + 0x48], %f36
	ldd	[%o1 + 0x50], %f38
	ldd	[%o1 + 0x58], %f40
	ldd	[%o1 + 0x60], %f42
	ldd	[%o1 + 0x68], %f44
	ldd	[%o1 + 0x70], %f46
	ldd	[%o1 + 0x78], %f48
	subcc	%o2, 1, %o2		! done yet?
	ldd	[%o1 + 0x80], %f50
	add	%o1, 0x80, %o1
	prefetch [%o1 + 63], 20
	prefetch [%o1 + 64+63], 20

	faligndata %f18, %f20, %f16
	faligndata %f20, %f22, %f18
	faligndata %f22, %f24, %f20
	faligndata %f24, %f26, %f22
	faligndata %f26, %f28, %f24
	faligndata %f28, %f30, %f26
	faligndata %f30, %f32, %f28
	faligndata %f32, %f34, %f30
	faligndata %f34, %f36, %f32
	faligndata %f36, %f38, %f34
	faligndata %f38, %f40, %f36
	faligndata %f40, %f42, %f38
	faligndata %f42, %f44, %f40
	faligndata %f44, %f46, %f42
	faligndata %f46, %f48, %f44
	faligndata %f48, %f50, %f46

	.word	0x81b02860		! SHA512

	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
	for	%f50, %f50, %f18	! %f18=%f50

	ba	.Lhwfinish
	nop
___
$code.=<<___ if ($SZ==4); 		# SHA256
	ld	[%o0 + 0x00], %f0
	ld	[%o0 + 0x04], %f1
	ld	[%o0 + 0x08], %f2
	ld	[%o0 + 0x0c], %f3
	ld	[%o0 + 0x10], %f4
	ld	[%o0 + 0x14], %f5
	andcc	%o1, 0x7, %g0
	ld	[%o0 + 0x18], %f6
	bne,pn	%icc, .Lhwunaligned
	 ld	[%o0 + 0x1c], %f7

.Lhwloop:
	ldd	[%o1 + 0x00], %f8
	ldd	[%o1 + 0x08], %f10
	ldd	[%o1 + 0x10], %f12
	ldd	[%o1 + 0x18], %f14
	ldd	[%o1 + 0x20], %f16
	ldd	[%o1 + 0x28], %f18
	ldd	[%o1 + 0x30], %f20
	subcc	%o2, 1, %o2		! done yet?
	ldd	[%o1 + 0x38], %f22
	add	%o1, 0x40, %o1
	prefetch [%o1 + 63], 20

	.word	0x81b02840		! SHA256

	bne,pt	SIZE_T_CC, .Lhwloop
	nop

.Lhwfinish:
	st	%f0, [%o0 + 0x00]	! store context
	st	%f1, [%o0 + 0x04]
	st	%f2, [%o0 + 0x08]
	st	%f3, [%o0 + 0x0c]
	st	%f4, [%o0 + 0x10]
	st	%f5, [%o0 + 0x14]
	st	%f6, [%o0 + 0x18]
	retl
	 st	%f7, [%o0 + 0x1c]

.align	8
.Lhwunaligned:
	alignaddr %o1, %g0, %o1

	ldd	[%o1 + 0x00], %f10
.Lhwunaligned_loop:
	ldd	[%o1 + 0x08], %f12
	ldd	[%o1 + 0x10], %f14
	ldd	[%o1 + 0x18], %f16
	ldd	[%o1 + 0x20], %f18
	ldd	[%o1 + 0x28], %f20
	ldd	[%o1 + 0x30], %f22
	ldd	[%o1 + 0x38], %f24
	subcc	%o2, 1, %o2		! done yet?
	ldd	[%o1 + 0x40], %f26
	add	%o1, 0x40, %o1
	prefetch [%o1 + 63], 20

	faligndata %f10, %f12, %f8
	faligndata %f12, %f14, %f10
	faligndata %f14, %f16, %f12
	faligndata %f16, %f18, %f14
	faligndata %f18, %f20, %f16
	faligndata %f20, %f22, %f18
	faligndata %f22, %f24, %f20
	faligndata %f24, %f26, %f22

	.word	0x81b02840		! SHA256

	bne,pt	SIZE_T_CC, .Lhwunaligned_loop
	for	%f26, %f26, %f10	! %f10=%f26

	ba	.Lhwfinish
	nop
___
$code.=<<___;
.align	16
.Lsoftware:
	save	%sp,-STACK_FRAME-$locals,%sp
	and	$inp,`$align-1`,$tmp31
	sllx	$len,`log(16*$SZ)/log(2)`,$len
	andn	$inp,`$align-1`,$inp
	sll	$tmp31,3,$tmp31
	add	$inp,$len,$len
___
$code.=<<___ if ($SZ==8); # SHA512
	mov	32,$tmp32
	sub	$tmp32,$tmp31,$tmp32
___
$code.=<<___;
.Lpic:	call	.+8
	add	%o7,K${label}-.Lpic,$Ktbl

	$LD	[$ctx+`0*$SZ`],$A
	$LD	[$ctx+`1*$SZ`],$B
	$LD	[$ctx+`2*$SZ`],$C
	$LD	[$ctx+`3*$SZ`],$D
	$LD	[$ctx+`4*$SZ`],$E
	$LD	[$ctx+`5*$SZ`],$F
	$LD	[$ctx+`6*$SZ`],$G
	$LD	[$ctx+`7*$SZ`],$H

.Lloop:
___
for ($i=0;$i<16;$i++)	{ &BODY_00_15($i,@V); unshift(@V,pop(@V)); }
$code.=".L16_xx:\n";
for (;$i<32;$i++)	{ &$BODY_16_XX($i,@V); unshift(@V,pop(@V)); }
$code.=<<___;
	and	$tmp2,0xfff,$tmp2
	cmp	$tmp2,$lastK
	bne	.L16_xx
	add	$Ktbl,`16*$SZ`,$Ktbl	! Ktbl+=16

___
$code.=<<___ if ($SZ==4); # SHA256
	$LD	[$ctx+`0*$SZ`],@X[0]
	$LD	[$ctx+`1*$SZ`],@X[1]
	$LD	[$ctx+`2*$SZ`],@X[2]
	$LD	[$ctx+`3*$SZ`],@X[3]
	$LD	[$ctx+`4*$SZ`],@X[4]
	$LD	[$ctx+`5*$SZ`],@X[5]
	$LD	[$ctx+`6*$SZ`],@X[6]
	$LD	[$ctx+`7*$SZ`],@X[7]

	add	$A,@X[0],$A
	$ST	$A,[$ctx+`0*$SZ`]
	add	$B,@X[1],$B
	$ST	$B,[$ctx+`1*$SZ`]
	add	$C,@X[2],$C
	$ST	$C,[$ctx+`2*$SZ`]
	add	$D,@X[3],$D
	$ST	$D,[$ctx+`3*$SZ`]
	add	$E,@X[4],$E
	$ST	$E,[$ctx+`4*$SZ`]
	add	$F,@X[5],$F
	$ST	$F,[$ctx+`5*$SZ`]
	add	$G,@X[6],$G
	$ST	$G,[$ctx+`6*$SZ`]
	add	$H,@X[7],$H
	$ST	$H,[$ctx+`7*$SZ`]
___
$code.=<<___ if ($SZ==8); # SHA512
	ld	[$ctx+`0*$SZ+0`],%l0
	ld	[$ctx+`0*$SZ+4`],%l1
	ld	[$ctx+`1*$SZ+0`],%l2
	ld	[$ctx+`1*$SZ+4`],%l3
	ld	[$ctx+`2*$SZ+0`],%l4
	ld	[$ctx+`2*$SZ+4`],%l5
	ld	[$ctx+`3*$SZ+0`],%l6

	sllx	%l0,32,$tmp0
	ld	[$ctx+`3*$SZ+4`],%l7
	sllx	%l2,32,$tmp1
	or	%l1,$tmp0,$tmp0
	or	%l3,$tmp1,$tmp1
	add	$tmp0,$A,$A
	add	$tmp1,$B,$B
	$ST	$A,[$ctx+`0*$SZ`]
	sllx	%l4,32,$tmp2
	$ST	$B,[$ctx+`1*$SZ`]
	sllx	%l6,32,$T1
	or	%l5,$tmp2,$tmp2
	or	%l7,$T1,$T1
	add	$tmp2,$C,$C
	$ST	$C,[$ctx+`2*$SZ`]
	add	$T1,$D,$D
	$ST	$D,[$ctx+`3*$SZ`]

	ld	[$ctx+`4*$SZ+0`],%l0
	ld	[$ctx+`4*$SZ+4`],%l1
	ld	[$ctx+`5*$SZ+0`],%l2
	ld	[$ctx+`5*$SZ+4`],%l3
	ld	[$ctx+`6*$SZ+0`],%l4
	ld	[$ctx+`6*$SZ+4`],%l5
	ld	[$ctx+`7*$SZ+0`],%l6

	sllx	%l0,32,$tmp0
	ld	[$ctx+`7*$SZ+4`],%l7
	sllx	%l2,32,$tmp1
	or	%l1,$tmp0,$tmp0
	or	%l3,$tmp1,$tmp1
	add	$tmp0,$E,$E
	add	$tmp1,$F,$F
	$ST	$E,[$ctx+`4*$SZ`]
	sllx	%l4,32,$tmp2
	$ST	$F,[$ctx+`5*$SZ`]
	sllx	%l6,32,$T1
	or	%l5,$tmp2,$tmp2
	or	%l7,$T1,$T1
	add	$tmp2,$G,$G
	$ST	$G,[$ctx+`6*$SZ`]
	add	$T1,$H,$H
	$ST	$H,[$ctx+`7*$SZ`]
___
$code.=<<___;
	add	$inp,`16*$SZ`,$inp		! advance inp
	cmp	$inp,$len
	bne	SIZE_T_CC,.Lloop
	sub	$Ktbl,`($rounds-16)*$SZ`,$Ktbl	! rewind Ktbl

	ret
	restore
.type	sha${label}_block_data_order,#function
.size	sha${label}_block_data_order,(.-sha${label}_block_data_order)
.asciz	"SHA${label} block transform for SPARCv9, CRYPTOGAMS by <appro\@openssl.org>"
.align	4
___

# Purpose of these subroutines is to explicitly encode VIS instructions,
# so that one can compile the module without having to specify VIS
# extensions on compiler command line, e.g. -xarch=v9 vs. -xarch=v9a.
# Idea is to reserve for option to produce "universal" binary and let
# programmer detect if current CPU is VIS capable at run-time.
sub unvis {
my ($mnemonic,$rs1,$rs2,$rd)=@_;
my $ref,$opf;
my %visopf = (	"faligndata"	=> 0x048,
		"for"		=> 0x07c	);

    $ref = "$mnemonic\t$rs1,$rs2,$rd";

    if ($opf=$visopf{$mnemonic}) {
	foreach ($rs1,$rs2,$rd) {
	    return $ref if (!/%f([0-9]{1,2})/);
	    $_=$1;
	    if ($1>=32) {
		return $ref if ($1&1);
		# re-encode for upper double register addressing
		$_=($1|$1>>5)&31;
	    }
	}

	return	sprintf ".word\t0x%08x !%s",
			0x81b00000|$rd<<25|$rs1<<14|$opf<<5|$rs2,
			$ref;
    } else {
	return $ref;
    }
}
sub unalignaddr {
my ($mnemonic,$rs1,$rs2,$rd)=@_;
my %bias = ( "g" => 0, "o" => 8, "l" => 16, "i" => 24 );
my $ref="$mnemonic\t$rs1,$rs2,$rd";

    foreach ($rs1,$rs2,$rd) {
	if (/%([goli])([0-7])/)	{ $_=$bias{$1}+$2; }
	else			{ return $ref; }
    }
    return  sprintf ".word\t0x%08x !%s",
		    0x81b00300|$rd<<25|$rs1<<14|$rs2,
		    $ref;
}

foreach (split("\n",$code)) {
	s/\`([^\`]*)\`/eval $1/ge;

	s/\b(f[^\s]*)\s+(%f[0-9]{1,2}),\s*(%f[0-9]{1,2}),\s*(%f[0-9]{1,2})/
		&unvis($1,$2,$3,$4)
	 /ge;
	s/\b(alignaddr)\s+(%[goli][0-7]),\s*(%[goli][0-7]),\s*(%[goli][0-7])/
		&unalignaddr($1,$2,$3,$4)
	 /ge;

	print $_,"\n";
}

close STDOUT or die "error closing STDOUT: $!";