#! /usr/bin/env perl
# Copyright 2005-2016 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. 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
# performance 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.
#
# May 2012.
#
# Optimization including one of Pavel Semjanov's ideas, alternative
# Maj, resulted in >=5% improvement on most CPUs, +20% SHA256 and
# unfortunately -2% SHA512 on P4 [which nobody should care about
# that much].
#
# June 2012.
#
# Add SIMD code paths, see below for improvement coefficients. SSSE3
# code path was not attempted for SHA512, because improvement is not
# estimated to be high enough, noticeably less than 9%, to justify
# the effort, not on pre-AVX processors. [Obviously with exclusion
# for VIA Nano, but it has SHA512 instruction that is faster and
# should be used instead.] For reference, corresponding estimated
# upper limit for improvement for SSSE3 SHA256 is 28%. The fact that
# higher coefficients are observed on VIA Nano and Bulldozer has more
# to do with specifics of their architecture [which is topic for
# separate discussion].
#
# November 2012.
#
# Add AVX2 code path. Two consecutive input blocks are loaded to
# 256-bit %ymm registers, with data from first block to least
# significant 128-bit halves and data from second to most significant.
# The data is then processed with same SIMD instruction sequence as
# for AVX, but with %ymm as operands. Side effect is increased stack
# frame, 448 additional bytes in SHA256 and 1152 in SHA512, and 1.2KB
# code size increase.
#
# March 2014.
#
# Add support for Intel SHA Extensions.

######################################################################
# Current performance in cycles per processed byte (less is better):
#
#		SHA256	SSSE3       AVX/XOP(*)	    SHA512  AVX/XOP(*)
#
# AMD K8	14.9	-	    -		    9.57    -
# P4		17.3	-	    -		    30.8    -
# Core 2	15.6	13.8(+13%)  -		    9.97    -
# Westmere	14.8	12.3(+19%)  -		    9.58    -
# Sandy Bridge	17.4	14.2(+23%)  11.6(+50%(**))  11.2    8.10(+38%(**))
# Ivy Bridge	12.6	10.5(+20%)  10.3(+22%)	    8.17    7.22(+13%)
# Haswell	12.2	9.28(+31%)  7.80(+56%)	    7.66    5.40(+42%)
# Skylake	11.4	9.03(+26%)  7.70(+48%)      7.25    5.20(+40%)
# Bulldozer	21.1	13.6(+54%)  13.6(+54%(***)) 13.5    8.58(+57%)
# Ryzen		11.0	9.02(+22%)  2.05(+440%)     7.05    5.67(+20%)
# VIA Nano	23.0	16.5(+39%)  -		    14.7    -
# Atom		23.0	18.9(+22%)  -		    14.7    -
# Silvermont	27.4	20.6(+33%)  -               17.5    -
# Knights L	27.4	21.0(+30%)  19.6(+40%)	    17.5    12.8(+37%)
# Goldmont	18.9	14.3(+32%)  4.16(+350%)     12.0    -
#
# (*)	whichever best applicable, including SHAEXT;
# (**)	switch from ror to shrd stands for fair share of improvement;
# (***)	execution time is fully determined by remaining integer-only
#	part, body_00_15; reducing the amount of SIMD instructions
#	below certain limit makes no difference/sense; to conserve
#	space SHA256 XOP code path is therefore omitted;
#
# Modified from upstream OpenSSL to remove the XOP code.

# The first two arguments should always be the flavour and output file path.
if ($#ARGV < 1) { die "Not enough arguments provided.
  Two arguments are necessary: the flavour and the output file path."; }

$flavour = shift;
$output  = shift;

$win64=0; $win64=1 if ($flavour =~ /[nm]asm|mingw64/ || $output =~ /\.asm$/);

$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";

# In upstream, this is controlled by shelling out to the compiler to check
# versions, but BoringSSL is intended to be used with pre-generated perlasm
# output, so this isn't useful anyway.
#
# This file also has an AVX2 implementation, controlled by setting $avx to 2.
# For now, we intentionally disable it. While it gives a 13-16% perf boost, the
# CFI annotations are wrong. It allocates stack in a loop and should be
# rewritten to avoid this.
$avx = 1;
$shaext=1;
for (@ARGV) { $avx = 0 if (/-DMY_ASSEMBLER_IS_TOO_OLD_FOR_AVX/); }

open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
*STDOUT=*OUT;

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,$a3)=("%r12","%r13","%r14","%r15","%rdi");
	@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,$a3)=("%r12d","%r13d","%r14d","%r15d","%edi");
	@Sigma0=( 2,13,22);
	@Sigma1=( 6,11,25);
	@sigma0=( 7,18, 3);
	@sigma1=(17,19,10);
	$rounds=64;
}

$ctx="%rdi";	# 1st arg, zapped by $a3
$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) = @_;
  my $STRIDE=$SZ;
     $STRIDE += 16 if ($i%(16/$SZ)==(16/$SZ-1));

$code.=<<___;
	ror	\$`$Sigma1[2]-$Sigma1[1]`,$a0
	mov	$f,$a2

	xor	$e,$a0
	ror	\$`$Sigma0[2]-$Sigma0[1]`,$a1
	xor	$g,$a2			# f^g

	mov	$T1,`$SZ*($i&0xf)`(%rsp)
	xor	$a,$a1
	and	$e,$a2			# (f^g)&e

	ror	\$`$Sigma1[1]-$Sigma1[0]`,$a0
	add	$h,$T1			# T1+=h
	xor	$g,$a2			# Ch(e,f,g)=((f^g)&e)^g

	ror	\$`$Sigma0[1]-$Sigma0[0]`,$a1
	xor	$e,$a0
	add	$a2,$T1			# T1+=Ch(e,f,g)

	mov	$a,$a2
	add	($Tbl),$T1		# T1+=K[round]
	xor	$a,$a1

	xor	$b,$a2			# a^b, b^c in next round
	ror	\$$Sigma1[0],$a0	# Sigma1(e)
	mov	$b,$h

	and	$a2,$a3
	ror	\$$Sigma0[0],$a1	# Sigma0(a)
	add	$a0,$T1			# T1+=Sigma1(e)

	xor	$a3,$h			# h=Maj(a,b,c)=Ch(a^b,c,b)
	add	$T1,$d			# d+=T1
	add	$T1,$h			# h+=T1

	lea	$STRIDE($Tbl),$Tbl	# round++
___
$code.=<<___ if ($i<15);
	add	$a1,$h			# h+=Sigma0(a)
___
	($a2,$a3) = ($a3,$a2);
}

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),$a2

	mov	$a0,$T1
	ror	\$`$sigma0[1]-$sigma0[0]`,$a0
	add	$a1,$a			# modulo-scheduled h+=Sigma0(a)
	mov	$a2,$a1
	ror	\$`$sigma1[1]-$sigma1[0]`,$a2

	xor	$T1,$a0
	shr	\$$sigma0[2],$T1
	ror	\$$sigma0[0],$a0
	xor	$a1,$a2
	shr	\$$sigma1[2],$a1

	ror	\$$sigma1[0],$a2
	xor	$a0,$T1			# sigma0(X[(i+1)&0xf])
	xor	$a1,$a2			# sigma1(X[(i+14)&0xf])
	add	`$SZ*(($i+9)&0xf)`(%rsp),$T1

	add	`$SZ*($i&0xf)`(%rsp),$T1
	mov	$e,$a0
	add	$a2,$T1
	mov	$a,$a1
___
	&ROUND_00_15(@_);
}

$code=<<___;
.text

.extern	OPENSSL_ia32cap_P
.globl	$func
.type	$func,\@function,3
.align	16
$func:
.cfi_startproc
___
$code.=<<___ if ($SZ==4 || $avx);
	leaq	OPENSSL_ia32cap_P(%rip),%r11
	mov	0(%r11),%r9d
	mov	4(%r11),%r10d
	mov	8(%r11),%r11d
___
$code.=<<___ if ($SZ==4 && $shaext);
	test	\$`1<<29`,%r11d		# check for SHA
	jnz	.Lshaext_shortcut
___
    # XOP codepath removed.
$code.=<<___ if ($avx>1);
	and	\$`1<<8|1<<5|1<<3`,%r11d	# check for BMI2+AVX2+BMI1
	cmp	\$`1<<8|1<<5|1<<3`,%r11d
	je	.Lavx2_shortcut
___
$code.=<<___ if ($avx);
	and	\$`1<<30`,%r9d		# mask "Intel CPU" bit
	and	\$`1<<28|1<<9`,%r10d	# mask AVX and SSSE3 bits
	or	%r9d,%r10d
	cmp	\$`1<<28|1<<9|1<<30`,%r10d
	je	.Lavx_shortcut
___
$code.=<<___ if ($SZ==4);
	test	\$`1<<9`,%r10d
	jnz	.Lssse3_shortcut
___
$code.=<<___;
	mov	%rsp,%rax		# copy %rsp
.cfi_def_cfa_register	%rax
	push	%rbx
.cfi_push	%rbx
	push	%rbp
.cfi_push	%rbp
	push	%r12
.cfi_push	%r12
	push	%r13
.cfi_push	%r13
	push	%r14
.cfi_push	%r14
	push	%r15
.cfi_push	%r15
	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	%rax,$_rsp		# save copy of %rsp
.cfi_cfa_expression	$_rsp,deref,+8
.Lprologue:

	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:
	mov	$B,$a3
	lea	$TABLE(%rip),$Tbl
	xor	$C,$a3			# magic
___
	for($i=0;$i<16;$i++) {
		$code.="	mov	$SZ*$i($inp),$T1\n";
		$code.="	mov	@ROT[4],$a0\n";
		$code.="	mov	@ROT[0],$a1\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.=<<___;
	cmpb	\$0,`$SZ-1`($Tbl)
	jnz	.Lrounds_16_xx

	mov	$_ctx,$ctx
	add	$a1,$A			# modulo-scheduled h+=Sigma0(a)
	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,%rsi
.cfi_def_cfa	%rsi,8
	mov	-48(%rsi),%r15
.cfi_restore	%r15
	mov	-40(%rsi),%r14
.cfi_restore	%r14
	mov	-32(%rsi),%r13
.cfi_restore	%r13
	mov	-24(%rsi),%r12
.cfi_restore	%r12
	mov	-16(%rsi),%rbp
.cfi_restore	%rbp
	mov	-8(%rsi),%rbx
.cfi_restore	%rbx
	lea	(%rsi),%rsp
.cfi_def_cfa_register	%rsp
.Lepilogue:
	ret
.cfi_endproc
.size	$func,.-$func
___

if ($SZ==4) {
$code.=<<___;
.section .rodata
.align	64
.type	$TABLE,\@object
$TABLE:
	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
	.long	0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5
	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
	.long	0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5
	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
	.long	0xd807aa98,0x12835b01,0x243185be,0x550c7dc3
	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
	.long	0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174
	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
	.long	0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc
	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
	.long	0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da
	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
	.long	0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7
	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
	.long	0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967
	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
	.long	0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13
	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
	.long	0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85
	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
	.long	0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3
	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
	.long	0xd192e819,0xd6990624,0xf40e3585,0x106aa070
	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
	.long	0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5
	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
	.long	0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3
	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
	.long	0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208
	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
	.long	0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2

	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
	.long	0x00010203,0x04050607,0x08090a0b,0x0c0d0e0f
	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
	.long	0x03020100,0x0b0a0908,0xffffffff,0xffffffff
	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
	.long	0xffffffff,0xffffffff,0x03020100,0x0b0a0908
	.asciz	"SHA256 block transform for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
.text
___
} else {
$code.=<<___;
.section .rodata
.align	64
.type	$TABLE,\@object
$TABLE:
	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
	.quad	0x428a2f98d728ae22,0x7137449123ef65cd
	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
	.quad	0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc
	.quad	0x3956c25bf348b538,0x59f111f1b605d019
	.quad	0x3956c25bf348b538,0x59f111f1b605d019
	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
	.quad	0x923f82a4af194f9b,0xab1c5ed5da6d8118
	.quad	0xd807aa98a3030242,0x12835b0145706fbe
	.quad	0xd807aa98a3030242,0x12835b0145706fbe
	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
	.quad	0x243185be4ee4b28c,0x550c7dc3d5ffb4e2
	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
	.quad	0x72be5d74f27b896f,0x80deb1fe3b1696b1
	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
	.quad	0x9bdc06a725c71235,0xc19bf174cf692694
	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
	.quad	0xe49b69c19ef14ad2,0xefbe4786384f25e3
	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
	.quad	0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65
	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
	.quad	0x2de92c6f592b0275,0x4a7484aa6ea6e483
	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
	.quad	0x5cb0a9dcbd41fbd4,0x76f988da831153b5
	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
	.quad	0x983e5152ee66dfab,0xa831c66d2db43210
	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
	.quad	0xb00327c898fb213f,0xbf597fc7beef0ee4
	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
	.quad	0xc6e00bf33da88fc2,0xd5a79147930aa725
	.quad	0x06ca6351e003826f,0x142929670a0e6e70
	.quad	0x06ca6351e003826f,0x142929670a0e6e70
	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
	.quad	0x27b70a8546d22ffc,0x2e1b21385c26c926
	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
	.quad	0x4d2c6dfc5ac42aed,0x53380d139d95b3df
	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
	.quad	0x650a73548baf63de,0x766a0abb3c77b2a8
	.quad	0x81c2c92e47edaee6,0x92722c851482353b
	.quad	0x81c2c92e47edaee6,0x92722c851482353b
	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
	.quad	0xa2bfe8a14cf10364,0xa81a664bbc423001
	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
	.quad	0xc24b8b70d0f89791,0xc76c51a30654be30
	.quad	0xd192e819d6ef5218,0xd69906245565a910
	.quad	0xd192e819d6ef5218,0xd69906245565a910
	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
	.quad	0xf40e35855771202a,0x106aa07032bbd1b8
	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
	.quad	0x19a4c116b8d2d0c8,0x1e376c085141ab53
	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
	.quad	0x2748774cdf8eeb99,0x34b0bcb5e19b48a8
	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
	.quad	0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb
	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
	.quad	0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3
	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
	.quad	0x748f82ee5defb2fc,0x78a5636f43172f60
	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
	.quad	0x84c87814a1f0ab72,0x8cc702081a6439ec
	.quad	0x90befffa23631e28,0xa4506cebde82bde9
	.quad	0x90befffa23631e28,0xa4506cebde82bde9
	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
	.quad	0xbef9a3f7b2c67915,0xc67178f2e372532b
	.quad	0xca273eceea26619c,0xd186b8c721c0c207
	.quad	0xca273eceea26619c,0xd186b8c721c0c207
	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
	.quad	0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178
	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
	.quad	0x06f067aa72176fba,0x0a637dc5a2c898a6
	.quad	0x113f9804bef90dae,0x1b710b35131c471b
	.quad	0x113f9804bef90dae,0x1b710b35131c471b
	.quad	0x28db77f523047d84,0x32caab7b40c72493
	.quad	0x28db77f523047d84,0x32caab7b40c72493
	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
	.quad	0x3c9ebe0a15c9bebc,0x431d67c49c100d4c
	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
	.quad	0x4cc5d4becb3e42b6,0x597f299cfc657e2a
	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817
	.quad	0x5fcb6fab3ad6faec,0x6c44198c4a475817

	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
	.quad	0x0001020304050607,0x08090a0b0c0d0e0f
	.asciz	"SHA512 block transform for x86_64, CRYPTOGAMS by <appro\@openssl.org>"
.text
___
}

######################################################################
# SIMD code paths
#
if ($SZ==4 && $shaext) {{{
######################################################################
# Intel SHA Extensions implementation of SHA256 update function.
#
my ($ctx,$inp,$num,$Tbl)=("%rdi","%rsi","%rdx","%rcx");

my ($Wi,$ABEF,$CDGH,$TMP,$BSWAP,$ABEF_SAVE,$CDGH_SAVE)=map("%xmm$_",(0..2,7..10));
my @MSG=map("%xmm$_",(3..6));

$code.=<<___;
.type	sha256_block_data_order_shaext,\@function,3
.align	64
sha256_block_data_order_shaext:
.Lshaext_shortcut:
.cfi_startproc
#ifdef BORINGSSL_DISPATCH_TEST
.extern BORINGSSL_function_hit
	movb \$1,BORINGSSL_function_hit+6(%rip)
#endif
___
$code.=<<___ if ($win64);
	lea	`-8-5*16`(%rsp),%rsp
	movaps	%xmm6,-8-5*16(%rax)
	movaps	%xmm7,-8-4*16(%rax)
	movaps	%xmm8,-8-3*16(%rax)
	movaps	%xmm9,-8-2*16(%rax)
	movaps	%xmm10,-8-1*16(%rax)
.Lprologue_shaext:
___
$code.=<<___;
	lea		K256+0x80(%rip),$Tbl
	movdqu		($ctx),$ABEF		# DCBA
	movdqu		16($ctx),$CDGH		# HGFE
	movdqa		0x200-0x80($Tbl),$TMP	# byte swap mask

	pshufd		\$0x1b,$ABEF,$Wi	# ABCD
	pshufd		\$0xb1,$ABEF,$ABEF	# CDAB
	pshufd		\$0x1b,$CDGH,$CDGH	# EFGH
	movdqa		$TMP,$BSWAP		# offload
	palignr		\$8,$CDGH,$ABEF		# ABEF
	punpcklqdq	$Wi,$CDGH		# CDGH
	jmp		.Loop_shaext

.align	16
.Loop_shaext:
	movdqu		($inp),@MSG[0]
	movdqu		0x10($inp),@MSG[1]
	movdqu		0x20($inp),@MSG[2]
	pshufb		$TMP,@MSG[0]
	movdqu		0x30($inp),@MSG[3]

	movdqa		0*32-0x80($Tbl),$Wi
	paddd		@MSG[0],$Wi
	pshufb		$TMP,@MSG[1]
	movdqa		$CDGH,$CDGH_SAVE	# offload
	sha256rnds2	$ABEF,$CDGH		# 0-3
	pshufd		\$0x0e,$Wi,$Wi
	nop
	movdqa		$ABEF,$ABEF_SAVE	# offload
	sha256rnds2	$CDGH,$ABEF

	movdqa		1*32-0x80($Tbl),$Wi
	paddd		@MSG[1],$Wi
	pshufb		$TMP,@MSG[2]
	sha256rnds2	$ABEF,$CDGH		# 4-7
	pshufd		\$0x0e,$Wi,$Wi
	lea		0x40($inp),$inp
	sha256msg1	@MSG[1],@MSG[0]
	sha256rnds2	$CDGH,$ABEF

	movdqa		2*32-0x80($Tbl),$Wi
	paddd		@MSG[2],$Wi
	pshufb		$TMP,@MSG[3]
	sha256rnds2	$ABEF,$CDGH		# 8-11
	pshufd		\$0x0e,$Wi,$Wi
	movdqa		@MSG[3],$TMP
	palignr		\$4,@MSG[2],$TMP
	nop
	paddd		$TMP,@MSG[0]
	sha256msg1	@MSG[2],@MSG[1]
	sha256rnds2	$CDGH,$ABEF

	movdqa		3*32-0x80($Tbl),$Wi
	paddd		@MSG[3],$Wi
	sha256msg2	@MSG[3],@MSG[0]
	sha256rnds2	$ABEF,$CDGH		# 12-15
	pshufd		\$0x0e,$Wi,$Wi
	movdqa		@MSG[0],$TMP
	palignr		\$4,@MSG[3],$TMP
	nop
	paddd		$TMP,@MSG[1]
	sha256msg1	@MSG[3],@MSG[2]
	sha256rnds2	$CDGH,$ABEF
___
for($i=4;$i<16-3;$i++) {
$code.=<<___;
	movdqa		$i*32-0x80($Tbl),$Wi
	paddd		@MSG[0],$Wi
	sha256msg2	@MSG[0],@MSG[1]
	sha256rnds2	$ABEF,$CDGH		# 16-19...
	pshufd		\$0x0e,$Wi,$Wi
	movdqa		@MSG[1],$TMP
	palignr		\$4,@MSG[0],$TMP
	nop
	paddd		$TMP,@MSG[2]
	sha256msg1	@MSG[0],@MSG[3]
	sha256rnds2	$CDGH,$ABEF
___
	push(@MSG,shift(@MSG));
}
$code.=<<___;
	movdqa		13*32-0x80($Tbl),$Wi
	paddd		@MSG[0],$Wi
	sha256msg2	@MSG[0],@MSG[1]
	sha256rnds2	$ABEF,$CDGH		# 52-55
	pshufd		\$0x0e,$Wi,$Wi
	movdqa		@MSG[1],$TMP
	palignr		\$4,@MSG[0],$TMP
	sha256rnds2	$CDGH,$ABEF
	paddd		$TMP,@MSG[2]

	movdqa		14*32-0x80($Tbl),$Wi
	paddd		@MSG[1],$Wi
	sha256rnds2	$ABEF,$CDGH		# 56-59
	pshufd		\$0x0e,$Wi,$Wi
	sha256msg2	@MSG[1],@MSG[2]
	movdqa		$BSWAP,$TMP
	sha256rnds2	$CDGH,$ABEF

	movdqa		15*32-0x80($Tbl),$Wi
	paddd		@MSG[2],$Wi
	nop
	sha256rnds2	$ABEF,$CDGH		# 60-63
	pshufd		\$0x0e,$Wi,$Wi
	dec		$num
	nop
	sha256rnds2	$CDGH,$ABEF

	paddd		$CDGH_SAVE,$CDGH
	paddd		$ABEF_SAVE,$ABEF
	jnz		.Loop_shaext

	pshufd		\$0xb1,$CDGH,$CDGH	# DCHG
	pshufd		\$0x1b,$ABEF,$TMP	# FEBA
	pshufd		\$0xb1,$ABEF,$ABEF	# BAFE
	punpckhqdq	$CDGH,$ABEF		# DCBA
	palignr		\$8,$TMP,$CDGH		# HGFE

	movdqu	$ABEF,($ctx)
	movdqu	$CDGH,16($ctx)
___
$code.=<<___ if ($win64);
	movaps	-8-5*16(%rax),%xmm6
	movaps	-8-4*16(%rax),%xmm7
	movaps	-8-3*16(%rax),%xmm8
	movaps	-8-2*16(%rax),%xmm9
	movaps	-8-1*16(%rax),%xmm10
	mov	%rax,%rsp
.Lepilogue_shaext:
___
$code.=<<___;
	ret
.cfi_endproc
.size	sha256_block_data_order_shaext,.-sha256_block_data_order_shaext
___
}}}
{{{

my $a4=$T1;
my ($a,$b,$c,$d,$e,$f,$g,$h);

sub AUTOLOAD()		# thunk [simplified] 32-bit style perlasm
{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://;
  my $arg = pop;
    $arg = "\$$arg" if ($arg*1 eq $arg);
    $code .= "\t$opcode\t".join(',',$arg,reverse @_)."\n";
}

sub body_00_15 () {
	(
	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.

	'&ror	($a0,$Sigma1[2]-$Sigma1[1])',
	'&mov	($a,$a1)',
	'&mov	($a4,$f)',

	'&ror	($a1,$Sigma0[2]-$Sigma0[1])',
	'&xor	($a0,$e)',
	'&xor	($a4,$g)',			# f^g

	'&ror	($a0,$Sigma1[1]-$Sigma1[0])',
	'&xor	($a1,$a)',
	'&and	($a4,$e)',			# (f^g)&e

	'&xor	($a0,$e)',
	'&add	($h,$SZ*($i&15)."(%rsp)")',	# h+=X[i]+K[i]
	'&mov	($a2,$a)',

	'&xor	($a4,$g)',			# Ch(e,f,g)=((f^g)&e)^g
	'&ror	($a1,$Sigma0[1]-$Sigma0[0])',
	'&xor	($a2,$b)',			# a^b, b^c in next round

	'&add	($h,$a4)',			# h+=Ch(e,f,g)
	'&ror	($a0,$Sigma1[0])',		# Sigma1(e)
	'&and	($a3,$a2)',			# (b^c)&(a^b)

	'&xor	($a1,$a)',
	'&add	($h,$a0)',			# h+=Sigma1(e)
	'&xor	($a3,$b)',			# Maj(a,b,c)=Ch(a^b,c,b)

	'&ror	($a1,$Sigma0[0])',		# Sigma0(a)
	'&add	($d,$h)',			# d+=h
	'&add	($h,$a3)',			# h+=Maj(a,b,c)

	'&mov	($a0,$d)',
	'&add	($a1,$h);'.			# h+=Sigma0(a)
	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
	);
}

######################################################################
# SSSE3 code path
#
if ($SZ==4) {	# SHA256 only
my @X = map("%xmm$_",(0..3));
my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));

$code.=<<___;
.type	${func}_ssse3,\@function,3
.align	64
${func}_ssse3:
.cfi_startproc
.Lssse3_shortcut:
	mov	%rsp,%rax		# copy %rsp
.cfi_def_cfa_register	%rax
	push	%rbx
.cfi_push	%rbx
	push	%rbp
.cfi_push	%rbp
	push	%r12
.cfi_push	%r12
	push	%r13
.cfi_push	%r13
	push	%r14
.cfi_push	%r14
	push	%r15
.cfi_push	%r15
	shl	\$4,%rdx		# num*16
	sub	\$`$framesz+$win64*16*4`,%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	%rax,$_rsp		# save copy of %rsp
.cfi_cfa_expression	$_rsp,deref,+8
___
$code.=<<___ if ($win64);
	movaps	%xmm6,16*$SZ+32(%rsp)
	movaps	%xmm7,16*$SZ+48(%rsp)
	movaps	%xmm8,16*$SZ+64(%rsp)
	movaps	%xmm9,16*$SZ+80(%rsp)
___
$code.=<<___;
.Lprologue_ssse3:

	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
___

$code.=<<___;
	#movdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
	#movdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
	jmp	.Lloop_ssse3
.align	16
.Lloop_ssse3:
	movdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
	movdqu	0x00($inp),@X[0]
	movdqu	0x10($inp),@X[1]
	movdqu	0x20($inp),@X[2]
	pshufb	$t3,@X[0]
	movdqu	0x30($inp),@X[3]
	lea	$TABLE(%rip),$Tbl
	pshufb	$t3,@X[1]
	movdqa	0x00($Tbl),$t0
	movdqa	0x20($Tbl),$t1
	pshufb	$t3,@X[2]
	paddd	@X[0],$t0
	movdqa	0x40($Tbl),$t2
	pshufb	$t3,@X[3]
	movdqa	0x60($Tbl),$t3
	paddd	@X[1],$t1
	paddd	@X[2],$t2
	paddd	@X[3],$t3
	movdqa	$t0,0x00(%rsp)
	mov	$A,$a1
	movdqa	$t1,0x10(%rsp)
	mov	$B,$a3
	movdqa	$t2,0x20(%rsp)
	xor	$C,$a3			# magic
	movdqa	$t3,0x30(%rsp)
	mov	$E,$a0
	jmp	.Lssse3_00_47

.align	16
.Lssse3_00_47:
	sub	\$`-16*2*$SZ`,$Tbl	# size optimization
___
sub Xupdate_256_SSSE3 () {
	(
	'&movdqa	($t0,@X[1]);',
	'&movdqa	($t3,@X[3])',
	'&palignr	($t0,@X[0],$SZ)',	# X[1..4]
	 '&palignr	($t3,@X[2],$SZ);',	# X[9..12]
	'&movdqa	($t1,$t0)',
	'&movdqa	($t2,$t0);',
	'&psrld		($t0,$sigma0[2])',
	 '&paddd	(@X[0],$t3);',		# X[0..3] += X[9..12]
	'&psrld		($t2,$sigma0[0])',
	 '&pshufd	($t3,@X[3],0b11111010)',# X[14..15]
	'&pslld		($t1,8*$SZ-$sigma0[1]);'.
	'&pxor		($t0,$t2)',
	'&psrld		($t2,$sigma0[1]-$sigma0[0]);'.
	'&pxor		($t0,$t1)',
	'&pslld		($t1,$sigma0[1]-$sigma0[0]);'.
	'&pxor		($t0,$t2);',
	 '&movdqa	($t2,$t3)',
	'&pxor		($t0,$t1);',		# sigma0(X[1..4])
	 '&psrld	($t3,$sigma1[2])',
	'&paddd		(@X[0],$t0);',		# X[0..3] += sigma0(X[1..4])
	 '&psrlq	($t2,$sigma1[0])',
	 '&pxor		($t3,$t2);',
	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
	 '&pxor		($t3,$t2)',
	 '&pshufb	($t3,$t4)',		# sigma1(X[14..15])
	'&paddd		(@X[0],$t3)',		# X[0..1] += sigma1(X[14..15])
	 '&pshufd	($t3,@X[0],0b01010000)',# X[16..17]
	 '&movdqa	($t2,$t3);',
	 '&psrld	($t3,$sigma1[2])',
	 '&psrlq	($t2,$sigma1[0])',
	 '&pxor		($t3,$t2);',
	 '&psrlq	($t2,$sigma1[1]-$sigma1[0])',
	 '&pxor		($t3,$t2);',
	'&movdqa	($t2,16*2*$j."($Tbl)")',
	 '&pshufb	($t3,$t5)',
	'&paddd		(@X[0],$t3)'		# X[2..3] += sigma1(X[16..17])
	);
}

sub SSSE3_256_00_47 () {
my $j = shift;
my $body = shift;
my @X = @_;
my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions

    if (0) {
	foreach (Xupdate_256_SSSE3()) {		# 36 instructions
	    eval;
	    eval(shift(@insns));
	    eval(shift(@insns));
	    eval(shift(@insns));
	}
    } else {			# squeeze extra 4% on Westmere and 19% on Atom
	  eval(shift(@insns));	#@
	&movdqa		($t0,@X[1]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&movdqa		($t3,@X[3]);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	&palignr	($t0,@X[0],$SZ);	# X[1..4]
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &palignr	($t3,@X[2],$SZ);	# X[9..12]
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	&movdqa		($t1,$t0);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&movdqa		($t2,$t0);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	&psrld		($t0,$sigma0[2]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &paddd		(@X[0],$t3);		# X[0..3] += X[9..12]
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	&psrld		($t2,$sigma0[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pshufd	($t3,@X[3],0b11111010);	# X[4..15]
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	&pslld		($t1,8*$SZ-$sigma0[1]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&pxor		($t0,$t2);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	&psrld		($t2,$sigma0[1]-$sigma0[0]);
	  eval(shift(@insns));
	&pxor		($t0,$t1);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&pslld		($t1,$sigma0[1]-$sigma0[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&pxor		($t0,$t2);
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	 &movdqa	($t2,$t3);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&pxor		($t0,$t1);		# sigma0(X[1..4])
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &psrld		($t3,$sigma1[2]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&paddd		(@X[0],$t0);		# X[0..3] += sigma0(X[1..4])
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	 &psrlq		($t2,$sigma1[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pxor		($t3,$t2);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pxor		($t3,$t2);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	 #&pshufb	($t3,$t4);		# sigma1(X[14..15])
	 &pshufd	($t3,$t3,0b10000000);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &psrldq	($t3,8);
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	&paddd		(@X[0],$t3);		# X[0..1] += sigma1(X[14..15])
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pshufd	($t3,@X[0],0b01010000);	# X[16..17]
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	 &movdqa	($t2,$t3);
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &psrld		($t3,$sigma1[2]);
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	 &psrlq		($t2,$sigma1[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pxor		($t3,$t2);
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	 &psrlq		($t2,$sigma1[1]-$sigma1[0]);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	 &pxor		($t3,$t2);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));	#@
	 #&pshufb	($t3,$t5);
	 &pshufd	($t3,$t3,0b00001000);
	  eval(shift(@insns));
	  eval(shift(@insns));
	&movdqa		($t2,16*2*$j."($Tbl)");
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	 &pslldq	($t3,8);
	  eval(shift(@insns));
	  eval(shift(@insns));
	  eval(shift(@insns));
	&paddd		(@X[0],$t3);		# X[2..3] += sigma1(X[16..17])
	  eval(shift(@insns));	#@
	  eval(shift(@insns));
	  eval(shift(@insns));
    }
	&paddd		($t2,@X[0]);
	  foreach (@insns) { eval; }		# remaining instructions
	&movdqa		(16*$j."(%rsp)",$t2);
}

    for ($i=0,$j=0; $j<4; $j++) {
	&SSSE3_256_00_47($j,\&body_00_15,@X);
	push(@X,shift(@X));			# rotate(@X)
    }
	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
	&jne	(".Lssse3_00_47");

    for ($i=0; $i<16; ) {
	foreach(body_00_15()) { eval; }
    }
$code.=<<___;
	mov	$_ctx,$ctx
	mov	$a1,$A

	add	$SZ*0($ctx),$A
	lea	16*$SZ($inp),$inp
	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_ssse3

	mov	$_rsp,%rsi
.cfi_def_cfa	%rsi,8
___
$code.=<<___ if ($win64);
	movaps	16*$SZ+32(%rsp),%xmm6
	movaps	16*$SZ+48(%rsp),%xmm7
	movaps	16*$SZ+64(%rsp),%xmm8
	movaps	16*$SZ+80(%rsp),%xmm9
___
$code.=<<___;
	mov	-48(%rsi),%r15
.cfi_restore	%r15
	mov	-40(%rsi),%r14
.cfi_restore	%r14
	mov	-32(%rsi),%r13
.cfi_restore	%r13
	mov	-24(%rsi),%r12
.cfi_restore	%r12
	mov	-16(%rsi),%rbp
.cfi_restore	%rbp
	mov	-8(%rsi),%rbx
.cfi_restore	%rbx
	lea	(%rsi),%rsp
.cfi_def_cfa_register	%rsp
.Lepilogue_ssse3:
	ret
.cfi_endproc
.size	${func}_ssse3,.-${func}_ssse3
___
}

if ($avx) {{
######################################################################
# AVX+shrd code path
#
local *ror = sub { &shrd(@_[0],@_) };

$code.=<<___;
.type	${func}_avx,\@function,3
.align	64
${func}_avx:
.cfi_startproc
.Lavx_shortcut:
	mov	%rsp,%rax		# copy %rsp
.cfi_def_cfa_register	%rax
	push	%rbx
.cfi_push	%rbx
	push	%rbp
.cfi_push	%rbp
	push	%r12
.cfi_push	%r12
	push	%r13
.cfi_push	%r13
	push	%r14
.cfi_push	%r14
	push	%r15
.cfi_push	%r15
	shl	\$4,%rdx		# num*16
	sub	\$`$framesz+$win64*16*($SZ==4?4:6)`,%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	%rax,$_rsp		# save copy of %rsp
.cfi_cfa_expression	$_rsp,deref,+8
___
$code.=<<___ if ($win64);
	movaps	%xmm6,16*$SZ+32(%rsp)
	movaps	%xmm7,16*$SZ+48(%rsp)
	movaps	%xmm8,16*$SZ+64(%rsp)
	movaps	%xmm9,16*$SZ+80(%rsp)
___
$code.=<<___ if ($win64 && $SZ>4);
	movaps	%xmm10,16*$SZ+96(%rsp)
	movaps	%xmm11,16*$SZ+112(%rsp)
___
$code.=<<___;
.Lprologue_avx:

	vzeroupper
	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
___
					if ($SZ==4) {	# SHA256
    my @X = map("%xmm$_",(0..3));
    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%xmm$_",(4..9));

$code.=<<___;
	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
	jmp	.Lloop_avx
.align	16
.Lloop_avx:
	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
	vmovdqu	0x00($inp),@X[0]
	vmovdqu	0x10($inp),@X[1]
	vmovdqu	0x20($inp),@X[2]
	vmovdqu	0x30($inp),@X[3]
	vpshufb	$t3,@X[0],@X[0]
	lea	$TABLE(%rip),$Tbl
	vpshufb	$t3,@X[1],@X[1]
	vpshufb	$t3,@X[2],@X[2]
	vpaddd	0x00($Tbl),@X[0],$t0
	vpshufb	$t3,@X[3],@X[3]
	vpaddd	0x20($Tbl),@X[1],$t1
	vpaddd	0x40($Tbl),@X[2],$t2
	vpaddd	0x60($Tbl),@X[3],$t3
	vmovdqa	$t0,0x00(%rsp)
	mov	$A,$a1
	vmovdqa	$t1,0x10(%rsp)
	mov	$B,$a3
	vmovdqa	$t2,0x20(%rsp)
	xor	$C,$a3			# magic
	vmovdqa	$t3,0x30(%rsp)
	mov	$E,$a0
	jmp	.Lavx_00_47

.align	16
.Lavx_00_47:
	sub	\$`-16*2*$SZ`,$Tbl	# size optimization
___
sub Xupdate_256_AVX () {
	(
	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..4]
	 '&vpalignr	($t3,@X[3],@X[2],$SZ)',	# X[9..12]
	'&vpsrld	($t2,$t0,$sigma0[0]);',
	 '&vpaddd	(@X[0],@X[0],$t3)',	# X[0..3] += X[9..12]
	'&vpsrld	($t3,$t0,$sigma0[2])',
	'&vpslld	($t1,$t0,8*$SZ-$sigma0[1]);',
	'&vpxor		($t0,$t3,$t2)',
	 '&vpshufd	($t3,@X[3],0b11111010)',# X[14..15]
	'&vpsrld	($t2,$t2,$sigma0[1]-$sigma0[0]);',
	'&vpxor		($t0,$t0,$t1)',
	'&vpslld	($t1,$t1,$sigma0[1]-$sigma0[0]);',
	'&vpxor		($t0,$t0,$t2)',
	 '&vpsrld	($t2,$t3,$sigma1[2]);',
	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..4])
	 '&vpsrlq	($t3,$t3,$sigma1[0]);',
	'&vpaddd	(@X[0],@X[0],$t0)',	# X[0..3] += sigma0(X[1..4])
	 '&vpxor	($t2,$t2,$t3);',
	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
	 '&vpxor	($t2,$t2,$t3)',
	 '&vpshufb	($t2,$t2,$t4)',		# sigma1(X[14..15])
	'&vpaddd	(@X[0],@X[0],$t2)',	# X[0..1] += sigma1(X[14..15])
	 '&vpshufd	($t3,@X[0],0b01010000)',# X[16..17]
	 '&vpsrld	($t2,$t3,$sigma1[2])',
	 '&vpsrlq	($t3,$t3,$sigma1[0])',
	 '&vpxor	($t2,$t2,$t3);',
	 '&vpsrlq	($t3,$t3,$sigma1[1]-$sigma1[0])',
	 '&vpxor	($t2,$t2,$t3)',
	 '&vpshufb	($t2,$t2,$t5)',
	'&vpaddd	(@X[0],@X[0],$t2)'	# X[2..3] += sigma1(X[16..17])
	);
}

sub AVX_256_00_47 () {
my $j = shift;
my $body = shift;
my @X = @_;
my @insns = (&$body,&$body,&$body,&$body);	# 104 instructions

	foreach (Xupdate_256_AVX()) {		# 29 instructions
	    eval;
	    eval(shift(@insns));
	    eval(shift(@insns));
	    eval(shift(@insns));
	}
	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
	  foreach (@insns) { eval; }		# remaining instructions
	&vmovdqa	(16*$j."(%rsp)",$t2);
}

    for ($i=0,$j=0; $j<4; $j++) {
	&AVX_256_00_47($j,\&body_00_15,@X);
	push(@X,shift(@X));			# rotate(@X)
    }
	&cmpb	($SZ-1+16*2*$SZ."($Tbl)",0);
	&jne	(".Lavx_00_47");

    for ($i=0; $i<16; ) {
	foreach(body_00_15()) { eval; }
    }

					} else {	# SHA512
    my @X = map("%xmm$_",(0..7));
    my ($t0,$t1,$t2,$t3) = map("%xmm$_",(8..11));

$code.=<<___;
	jmp	.Lloop_avx
.align	16
.Lloop_avx:
	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
	vmovdqu	0x00($inp),@X[0]
	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
	vmovdqu	0x10($inp),@X[1]
	vmovdqu	0x20($inp),@X[2]
	vpshufb	$t3,@X[0],@X[0]
	vmovdqu	0x30($inp),@X[3]
	vpshufb	$t3,@X[1],@X[1]
	vmovdqu	0x40($inp),@X[4]
	vpshufb	$t3,@X[2],@X[2]
	vmovdqu	0x50($inp),@X[5]
	vpshufb	$t3,@X[3],@X[3]
	vmovdqu	0x60($inp),@X[6]
	vpshufb	$t3,@X[4],@X[4]
	vmovdqu	0x70($inp),@X[7]
	vpshufb	$t3,@X[5],@X[5]
	vpaddq	-0x80($Tbl),@X[0],$t0
	vpshufb	$t3,@X[6],@X[6]
	vpaddq	-0x60($Tbl),@X[1],$t1
	vpshufb	$t3,@X[7],@X[7]
	vpaddq	-0x40($Tbl),@X[2],$t2
	vpaddq	-0x20($Tbl),@X[3],$t3
	vmovdqa	$t0,0x00(%rsp)
	vpaddq	0x00($Tbl),@X[4],$t0
	vmovdqa	$t1,0x10(%rsp)
	vpaddq	0x20($Tbl),@X[5],$t1
	vmovdqa	$t2,0x20(%rsp)
	vpaddq	0x40($Tbl),@X[6],$t2
	vmovdqa	$t3,0x30(%rsp)
	vpaddq	0x60($Tbl),@X[7],$t3
	vmovdqa	$t0,0x40(%rsp)
	mov	$A,$a1
	vmovdqa	$t1,0x50(%rsp)
	mov	$B,$a3
	vmovdqa	$t2,0x60(%rsp)
	xor	$C,$a3			# magic
	vmovdqa	$t3,0x70(%rsp)
	mov	$E,$a0
	jmp	.Lavx_00_47

.align	16
.Lavx_00_47:
	add	\$`16*2*$SZ`,$Tbl
___
sub Xupdate_512_AVX () {
	(
	'&vpalignr	($t0,@X[1],@X[0],$SZ)',	# X[1..2]
	 '&vpalignr	($t3,@X[5],@X[4],$SZ)',	# X[9..10]
	'&vpsrlq	($t2,$t0,$sigma0[0])',
	 '&vpaddq	(@X[0],@X[0],$t3);',	# X[0..1] += X[9..10]
	'&vpsrlq	($t3,$t0,$sigma0[2])',
	'&vpsllq	($t1,$t0,8*$SZ-$sigma0[1]);',
	 '&vpxor	($t0,$t3,$t2)',
	'&vpsrlq	($t2,$t2,$sigma0[1]-$sigma0[0]);',
	 '&vpxor	($t0,$t0,$t1)',
	'&vpsllq	($t1,$t1,$sigma0[1]-$sigma0[0]);',
	 '&vpxor	($t0,$t0,$t2)',
	 '&vpsrlq	($t3,@X[7],$sigma1[2]);',
	'&vpxor		($t0,$t0,$t1)',		# sigma0(X[1..2])
	 '&vpsllq	($t2,@X[7],8*$SZ-$sigma1[1]);',
	'&vpaddq	(@X[0],@X[0],$t0)',	# X[0..1] += sigma0(X[1..2])
	 '&vpsrlq	($t1,@X[7],$sigma1[0]);',
	 '&vpxor	($t3,$t3,$t2)',
	 '&vpsllq	($t2,$t2,$sigma1[1]-$sigma1[0]);',
	 '&vpxor	($t3,$t3,$t1)',
	 '&vpsrlq	($t1,$t1,$sigma1[1]-$sigma1[0]);',
	 '&vpxor	($t3,$t3,$t2)',
	 '&vpxor	($t3,$t3,$t1)',		# sigma1(X[14..15])
	'&vpaddq	(@X[0],@X[0],$t3)',	# X[0..1] += sigma1(X[14..15])
	);
}

sub AVX_512_00_47 () {
my $j = shift;
my $body = shift;
my @X = @_;
my @insns = (&$body,&$body);			# 52 instructions

	foreach (Xupdate_512_AVX()) {		# 23 instructions
	    eval;
	    eval(shift(@insns));
	    eval(shift(@insns));
	}
	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
	  foreach (@insns) { eval; }		# remaining instructions
	&vmovdqa	(16*$j."(%rsp)",$t2);
}

    for ($i=0,$j=0; $j<8; $j++) {
	&AVX_512_00_47($j,\&body_00_15,@X);
	push(@X,shift(@X));			# rotate(@X)
    }
	&cmpb	($SZ-1+16*2*$SZ-0x80."($Tbl)",0);
	&jne	(".Lavx_00_47");

    for ($i=0; $i<16; ) {
	foreach(body_00_15()) { eval; }
    }
}
$code.=<<___;
	mov	$_ctx,$ctx
	mov	$a1,$A

	add	$SZ*0($ctx),$A
	lea	16*$SZ($inp),$inp
	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_avx

	mov	$_rsp,%rsi
.cfi_def_cfa	%rsi,8
	vzeroupper
___
$code.=<<___ if ($win64);
	movaps	16*$SZ+32(%rsp),%xmm6
	movaps	16*$SZ+48(%rsp),%xmm7
	movaps	16*$SZ+64(%rsp),%xmm8
	movaps	16*$SZ+80(%rsp),%xmm9
___
$code.=<<___ if ($win64 && $SZ>4);
	movaps	16*$SZ+96(%rsp),%xmm10
	movaps	16*$SZ+112(%rsp),%xmm11
___
$code.=<<___;
	mov	-48(%rsi),%r15
.cfi_restore	%r15
	mov	-40(%rsi),%r14
.cfi_restore	%r14
	mov	-32(%rsi),%r13
.cfi_restore	%r13
	mov	-24(%rsi),%r12
.cfi_restore	%r12
	mov	-16(%rsi),%rbp
.cfi_restore	%rbp
	mov	-8(%rsi),%rbx
.cfi_restore	%rbx
	lea	(%rsi),%rsp
.cfi_def_cfa_register	%rsp
.Lepilogue_avx:
	ret
.cfi_endproc
.size	${func}_avx,.-${func}_avx
___

if ($avx>1) {{
######################################################################
# AVX2+BMI code path
#
my $a5=$SZ==4?"%esi":"%rsi";	# zap $inp
my $PUSH8=8*2*$SZ;
use integer;

sub bodyx_00_15 () {
	# at start $a1 should be zero, $a3 - $b^$c and $a4 copy of $f
	(
	'($a,$b,$c,$d,$e,$f,$g,$h)=@ROT;'.

	'&add	($h,(32*($i/(16/$SZ))+$SZ*($i%(16/$SZ)))%$PUSH8.$base)',    # h+=X[i]+K[i]
	'&and	($a4,$e)',		# f&e
	'&rorx	($a0,$e,$Sigma1[2])',
	'&rorx	($a2,$e,$Sigma1[1])',

	'&lea	($a,"($a,$a1)")',	# h+=Sigma0(a) from the past
	'&lea	($h,"($h,$a4)")',
	'&andn	($a4,$e,$g)',		# ~e&g
	'&xor	($a0,$a2)',

	'&rorx	($a1,$e,$Sigma1[0])',
	'&lea	($h,"($h,$a4)")',	# h+=Ch(e,f,g)=(e&f)+(~e&g)
	'&xor	($a0,$a1)',		# Sigma1(e)
	'&mov	($a2,$a)',

	'&rorx	($a4,$a,$Sigma0[2])',
	'&lea	($h,"($h,$a0)")',	# h+=Sigma1(e)
	'&xor	($a2,$b)',		# a^b, b^c in next round
	'&rorx	($a1,$a,$Sigma0[1])',

	'&rorx	($a0,$a,$Sigma0[0])',
	'&lea	($d,"($d,$h)")',	# d+=h
	'&and	($a3,$a2)',		# (b^c)&(a^b)
	'&xor	($a1,$a4)',

	'&xor	($a3,$b)',		# Maj(a,b,c)=Ch(a^b,c,b)
	'&xor	($a1,$a0)',		# Sigma0(a)
	'&lea	($h,"($h,$a3)");'.	# h+=Maj(a,b,c)
	'&mov	($a4,$e)',		# copy of f in future

	'($a2,$a3) = ($a3,$a2); unshift(@ROT,pop(@ROT)); $i++;'
	);
	# and at the finish one has to $a+=$a1
}

$code.=<<___;
.type	${func}_avx2,\@function,3
.align	64
${func}_avx2:
.cfi_startproc
.Lavx2_shortcut:
	mov	%rsp,%rax		# copy %rsp
.cfi_def_cfa_register	%rax
	push	%rbx
.cfi_push	%rbx
	push	%rbp
.cfi_push	%rbp
	push	%r12
.cfi_push	%r12
	push	%r13
.cfi_push	%r13
	push	%r14
.cfi_push	%r14
	push	%r15
.cfi_push	%r15
	sub	\$`2*$SZ*$rounds+4*8+$win64*16*($SZ==4?4:6)`,%rsp
	shl	\$4,%rdx		# num*16
	and	\$-256*$SZ,%rsp		# align stack frame
	lea	($inp,%rdx,$SZ),%rdx	# inp+num*16*$SZ
	add	\$`2*$SZ*($rounds-8)`,%rsp
	mov	$ctx,$_ctx		# save ctx, 1st arg
	mov	$inp,$_inp		# save inp, 2nd arh
	mov	%rdx,$_end		# save end pointer, "3rd" arg
	mov	%rax,$_rsp		# save copy of %rsp
.cfi_cfa_expression	$_rsp,deref,+8
___
$code.=<<___ if ($win64);
	movaps	%xmm6,16*$SZ+32(%rsp)
	movaps	%xmm7,16*$SZ+48(%rsp)
	movaps	%xmm8,16*$SZ+64(%rsp)
	movaps	%xmm9,16*$SZ+80(%rsp)
___
$code.=<<___ if ($win64 && $SZ>4);
	movaps	%xmm10,16*$SZ+96(%rsp)
	movaps	%xmm11,16*$SZ+112(%rsp)
___
$code.=<<___;
.Lprologue_avx2:

	vzeroupper
	sub	\$-16*$SZ,$inp		# inp++, size optimization
	mov	$SZ*0($ctx),$A
	mov	$inp,%r12		# borrow $T1
	mov	$SZ*1($ctx),$B
	cmp	%rdx,$inp		# $_end
	mov	$SZ*2($ctx),$C
	cmove	%rsp,%r12		# next block or random data
	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
___
					if ($SZ==4) {	# SHA256
    my @X = map("%ymm$_",(0..3));
    my ($t0,$t1,$t2,$t3, $t4,$t5) = map("%ymm$_",(4..9));

$code.=<<___;
	vmovdqa	$TABLE+`$SZ*2*$rounds`+32(%rip),$t4
	vmovdqa	$TABLE+`$SZ*2*$rounds`+64(%rip),$t5
	jmp	.Loop_avx2
.align	16
.Loop_avx2:
	vmovdqa	$TABLE+`$SZ*2*$rounds`(%rip),$t3
	vmovdqu	-16*$SZ+0($inp),%xmm0
	vmovdqu	-16*$SZ+16($inp),%xmm1
	vmovdqu	-16*$SZ+32($inp),%xmm2
	vmovdqu	-16*$SZ+48($inp),%xmm3
	#mov		$inp,$_inp	# offload $inp
	vinserti128	\$1,(%r12),@X[0],@X[0]
	vinserti128	\$1,16(%r12),@X[1],@X[1]
	vpshufb		$t3,@X[0],@X[0]
	vinserti128	\$1,32(%r12),@X[2],@X[2]
	vpshufb		$t3,@X[1],@X[1]
	vinserti128	\$1,48(%r12),@X[3],@X[3]

	lea	$TABLE(%rip),$Tbl
	vpshufb	$t3,@X[2],@X[2]
	vpaddd	0x00($Tbl),@X[0],$t0
	vpshufb	$t3,@X[3],@X[3]
	vpaddd	0x20($Tbl),@X[1],$t1
	vpaddd	0x40($Tbl),@X[2],$t2
	vpaddd	0x60($Tbl),@X[3],$t3
	vmovdqa	$t0,0x00(%rsp)
	xor	$a1,$a1
	vmovdqa	$t1,0x20(%rsp)
	lea	-$PUSH8(%rsp),%rsp
	mov	$B,$a3
	vmovdqa	$t2,0x00(%rsp)
	xor	$C,$a3			# magic
	vmovdqa	$t3,0x20(%rsp)
	mov	$F,$a4
	sub	\$-16*2*$SZ,$Tbl	# size optimization
	jmp	.Lavx2_00_47

.align	16
.Lavx2_00_47:
___

sub AVX2_256_00_47 () {
my $j = shift;
my $body = shift;
my @X = @_;
my @insns = (&$body,&$body,&$body,&$body);	# 96 instructions
my $base = "+2*$PUSH8(%rsp)";

	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%2)==0);
	foreach (Xupdate_256_AVX()) {		# 29 instructions
	    eval;
	    eval(shift(@insns));
	    eval(shift(@insns));
	    eval(shift(@insns));
	}
	&vpaddd		($t2,@X[0],16*2*$j."($Tbl)");
	  foreach (@insns) { eval; }		# remaining instructions
	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
}

    for ($i=0,$j=0; $j<4; $j++) {
	&AVX2_256_00_47($j,\&bodyx_00_15,@X);
	push(@X,shift(@X));			# rotate(@X)
    }
	&lea	($Tbl,16*2*$SZ."($Tbl)");
	&cmpb	(($SZ-1)."($Tbl)",0);
	&jne	(".Lavx2_00_47");

    for ($i=0; $i<16; ) {
	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
	foreach(bodyx_00_15()) { eval; }
    }
					} else {	# SHA512
    my @X = map("%ymm$_",(0..7));
    my ($t0,$t1,$t2,$t3) = map("%ymm$_",(8..11));

$code.=<<___;
	jmp	.Loop_avx2
.align	16
.Loop_avx2:
	vmovdqu	-16*$SZ($inp),%xmm0
	vmovdqu	-16*$SZ+16($inp),%xmm1
	vmovdqu	-16*$SZ+32($inp),%xmm2
	lea	$TABLE+0x80(%rip),$Tbl	# size optimization
	vmovdqu	-16*$SZ+48($inp),%xmm3
	vmovdqu	-16*$SZ+64($inp),%xmm4
	vmovdqu	-16*$SZ+80($inp),%xmm5
	vmovdqu	-16*$SZ+96($inp),%xmm6
	vmovdqu	-16*$SZ+112($inp),%xmm7
	#mov	$inp,$_inp	# offload $inp
	vmovdqa	`$SZ*2*$rounds-0x80`($Tbl),$t2
	vinserti128	\$1,(%r12),@X[0],@X[0]
	vinserti128	\$1,16(%r12),@X[1],@X[1]
	 vpshufb	$t2,@X[0],@X[0]
	vinserti128	\$1,32(%r12),@X[2],@X[2]
	 vpshufb	$t2,@X[1],@X[1]
	vinserti128	\$1,48(%r12),@X[3],@X[3]
	 vpshufb	$t2,@X[2],@X[2]
	vinserti128	\$1,64(%r12),@X[4],@X[4]
	 vpshufb	$t2,@X[3],@X[3]
	vinserti128	\$1,80(%r12),@X[5],@X[5]
	 vpshufb	$t2,@X[4],@X[4]
	vinserti128	\$1,96(%r12),@X[6],@X[6]
	 vpshufb	$t2,@X[5],@X[5]
	vinserti128	\$1,112(%r12),@X[7],@X[7]

	vpaddq	-0x80($Tbl),@X[0],$t0
	vpshufb	$t2,@X[6],@X[6]
	vpaddq	-0x60($Tbl),@X[1],$t1
	vpshufb	$t2,@X[7],@X[7]
	vpaddq	-0x40($Tbl),@X[2],$t2
	vpaddq	-0x20($Tbl),@X[3],$t3
	vmovdqa	$t0,0x00(%rsp)
	vpaddq	0x00($Tbl),@X[4],$t0
	vmovdqa	$t1,0x20(%rsp)
	vpaddq	0x20($Tbl),@X[5],$t1
	vmovdqa	$t2,0x40(%rsp)
	vpaddq	0x40($Tbl),@X[6],$t2
	vmovdqa	$t3,0x60(%rsp)
	lea	-$PUSH8(%rsp),%rsp
	vpaddq	0x60($Tbl),@X[7],$t3
	vmovdqa	$t0,0x00(%rsp)
	xor	$a1,$a1
	vmovdqa	$t1,0x20(%rsp)
	mov	$B,$a3
	vmovdqa	$t2,0x40(%rsp)
	xor	$C,$a3			# magic
	vmovdqa	$t3,0x60(%rsp)
	mov	$F,$a4
	add	\$16*2*$SZ,$Tbl
	jmp	.Lavx2_00_47

.align	16
.Lavx2_00_47:
___

sub AVX2_512_00_47 () {
my $j = shift;
my $body = shift;
my @X = @_;
my @insns = (&$body,&$body);			# 48 instructions
my $base = "+2*$PUSH8(%rsp)";

	&lea	("%rsp","-$PUSH8(%rsp)")	if (($j%4)==0);
	foreach (Xupdate_512_AVX()) {		# 23 instructions
	    eval;
	    if ($_ !~ /\;$/) {
		eval(shift(@insns));
		eval(shift(@insns));
		eval(shift(@insns));
	    }
	}
	&vpaddq		($t2,@X[0],16*2*$j-0x80."($Tbl)");
	  foreach (@insns) { eval; }		# remaining instructions
	&vmovdqa	((32*$j)%$PUSH8."(%rsp)",$t2);
}

    for ($i=0,$j=0; $j<8; $j++) {
	&AVX2_512_00_47($j,\&bodyx_00_15,@X);
	push(@X,shift(@X));			# rotate(@X)
    }
	&lea	($Tbl,16*2*$SZ."($Tbl)");
	&cmpb	(($SZ-1-0x80)."($Tbl)",0);
	&jne	(".Lavx2_00_47");

    for ($i=0; $i<16; ) {
	my $base=$i<8?"+$PUSH8(%rsp)":"(%rsp)";
	foreach(bodyx_00_15()) { eval; }
    }
}
$code.=<<___;
	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
	add	$a1,$A
	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
	lea	`2*$SZ*($rounds-8)`(%rsp),$Tbl

	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

	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)

	cmp	`$PUSH8+2*8`($Tbl),$inp	# $_end
	je	.Ldone_avx2

	xor	$a1,$a1
	mov	$B,$a3
	xor	$C,$a3			# magic
	mov	$F,$a4
	jmp	.Lower_avx2
.align	16
.Lower_avx2:
___
    for ($i=0; $i<8; ) {
	my $base="+16($Tbl)";
	foreach(bodyx_00_15()) { eval; }
    }
$code.=<<___;
	lea	-$PUSH8($Tbl),$Tbl
	cmp	%rsp,$Tbl
	jae	.Lower_avx2

	mov	`2*$SZ*$rounds`(%rsp),$ctx	# $_ctx
	add	$a1,$A
	#mov	`2*$SZ*$rounds+8`(%rsp),$inp	# $_inp
	lea	`2*$SZ*($rounds-8)`(%rsp),%rsp

	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
	lea	`2*16*$SZ`($inp),$inp	# inp+=2
	add	$SZ*6($ctx),$G
	mov	$inp,%r12
	add	$SZ*7($ctx),$H
	cmp	$_end,$inp

	mov	$A,$SZ*0($ctx)
	cmove	%rsp,%r12		# next block or stale data
	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)

	jbe	.Loop_avx2
	lea	(%rsp),$Tbl

.Ldone_avx2:
	lea	($Tbl),%rsp
	mov	$_rsp,%rsi
.cfi_def_cfa	%rsi,8
	vzeroupper
___
$code.=<<___ if ($win64);
	movaps	16*$SZ+32(%rsp),%xmm6
	movaps	16*$SZ+48(%rsp),%xmm7
	movaps	16*$SZ+64(%rsp),%xmm8
	movaps	16*$SZ+80(%rsp),%xmm9
___
$code.=<<___ if ($win64 && $SZ>4);
	movaps	16*$SZ+96(%rsp),%xmm10
	movaps	16*$SZ+112(%rsp),%xmm11
___
$code.=<<___;
	mov	-48(%rsi),%r15
.cfi_restore	%r15
	mov	-40(%rsi),%r14
.cfi_restore	%r14
	mov	-32(%rsi),%r13
.cfi_restore	%r13
	mov	-24(%rsi),%r12
.cfi_restore	%r12
	mov	-16(%rsi),%rbp
.cfi_restore	%rbp
	mov	-8(%rsi),%rbx
.cfi_restore	%rbx
	lea	(%rsi),%rsp
.cfi_def_cfa_register	%rsp
.Lepilogue_avx2:
	ret
.cfi_endproc
.size	${func}_avx2,.-${func}_avx2
___
}}
}}}}}

# EXCEPTION_DISPOSITION handler (EXCEPTION_RECORD *rec,ULONG64 frame,
#		CONTEXT *context,DISPATCHER_CONTEXT *disp)
if ($win64) {
$rec="%rcx";
$frame="%rdx";
$context="%r8";
$disp="%r9";

$code.=<<___;
.extern	__imp_RtlVirtualUnwind
.type	se_handler,\@abi-omnipotent
.align	16
se_handler:
	push	%rsi
	push	%rdi
	push	%rbx
	push	%rbp
	push	%r12
	push	%r13
	push	%r14
	push	%r15
	pushfq
	sub	\$64,%rsp

	mov	120($context),%rax	# pull context->Rax
	mov	248($context),%rbx	# pull context->Rip

	mov	8($disp),%rsi		# disp->ImageBase
	mov	56($disp),%r11		# disp->HanderlData

	mov	0(%r11),%r10d		# HandlerData[0]
	lea	(%rsi,%r10),%r10	# prologue label
	cmp	%r10,%rbx		# context->Rip<prologue label
	jb	.Lin_prologue

	mov	152($context),%rax	# pull context->Rsp

	mov	4(%r11),%r10d		# HandlerData[1]
	lea	(%rsi,%r10),%r10	# epilogue label
	cmp	%r10,%rbx		# context->Rip>=epilogue label
	jae	.Lin_prologue
___
$code.=<<___ if ($avx>1);
	lea	.Lavx2_shortcut(%rip),%r10
	cmp	%r10,%rbx		# context->Rip<avx2_shortcut
	jb	.Lnot_in_avx2

	and	\$-256*$SZ,%rax
	add	\$`2*$SZ*($rounds-8)`,%rax
.Lnot_in_avx2:
___
$code.=<<___;
	mov	%rax,%rsi		# put aside Rsp
	mov	16*$SZ+3*8(%rax),%rax	# pull $_rsp

	mov	-8(%rax),%rbx
	mov	-16(%rax),%rbp
	mov	-24(%rax),%r12
	mov	-32(%rax),%r13
	mov	-40(%rax),%r14
	mov	-48(%rax),%r15
	mov	%rbx,144($context)	# restore context->Rbx
	mov	%rbp,160($context)	# restore context->Rbp
	mov	%r12,216($context)	# restore context->R12
	mov	%r13,224($context)	# restore context->R13
	mov	%r14,232($context)	# restore context->R14
	mov	%r15,240($context)	# restore context->R15

	lea	.Lepilogue(%rip),%r10
	cmp	%r10,%rbx
	jb	.Lin_prologue		# non-AVX code

	lea	16*$SZ+4*8(%rsi),%rsi	# Xmm6- save area
	lea	512($context),%rdi	# &context.Xmm6
	mov	\$`$SZ==4?8:12`,%ecx
	.long	0xa548f3fc		# cld; rep movsq

.Lin_prologue:
	mov	8(%rax),%rdi
	mov	16(%rax),%rsi
	mov	%rax,152($context)	# restore context->Rsp
	mov	%rsi,168($context)	# restore context->Rsi
	mov	%rdi,176($context)	# restore context->Rdi

	mov	40($disp),%rdi		# disp->ContextRecord
	mov	$context,%rsi		# context
	mov	\$154,%ecx		# sizeof(CONTEXT)
	.long	0xa548f3fc		# cld; rep movsq

	mov	$disp,%rsi
	xor	%rcx,%rcx		# arg1, UNW_FLAG_NHANDLER
	mov	8(%rsi),%rdx		# arg2, disp->ImageBase
	mov	0(%rsi),%r8		# arg3, disp->ControlPc
	mov	16(%rsi),%r9		# arg4, disp->FunctionEntry
	mov	40(%rsi),%r10		# disp->ContextRecord
	lea	56(%rsi),%r11		# &disp->HandlerData
	lea	24(%rsi),%r12		# &disp->EstablisherFrame
	mov	%r10,32(%rsp)		# arg5
	mov	%r11,40(%rsp)		# arg6
	mov	%r12,48(%rsp)		# arg7
	mov	%rcx,56(%rsp)		# arg8, (NULL)
	call	*__imp_RtlVirtualUnwind(%rip)

	mov	\$1,%eax		# ExceptionContinueSearch
	add	\$64,%rsp
	popfq
	pop	%r15
	pop	%r14
	pop	%r13
	pop	%r12
	pop	%rbp
	pop	%rbx
	pop	%rdi
	pop	%rsi
	ret
.size	se_handler,.-se_handler
___

$code.=<<___ if ($SZ==4 && $shaext);
.type	shaext_handler,\@abi-omnipotent
.align	16
shaext_handler:
	push	%rsi
	push	%rdi
	push	%rbx
	push	%rbp
	push	%r12
	push	%r13
	push	%r14
	push	%r15
	pushfq
	sub	\$64,%rsp

	mov	120($context),%rax	# pull context->Rax
	mov	248($context),%rbx	# pull context->Rip

	lea	.Lprologue_shaext(%rip),%r10
	cmp	%r10,%rbx		# context->Rip<.Lprologue
	jb	.Lin_prologue

	lea	.Lepilogue_shaext(%rip),%r10
	cmp	%r10,%rbx		# context->Rip>=.Lepilogue
	jae	.Lin_prologue

	lea	-8-5*16(%rax),%rsi
	lea	512($context),%rdi	# &context.Xmm6
	mov	\$10,%ecx
	.long	0xa548f3fc		# cld; rep movsq

	jmp	.Lin_prologue
.size	shaext_handler,.-shaext_handler
___

$code.=<<___;
.section	.pdata
.align	4
	.rva	.LSEH_begin_$func
	.rva	.LSEH_end_$func
	.rva	.LSEH_info_$func
___
$code.=<<___ if ($SZ==4 && $shaext);
	.rva	.LSEH_begin_${func}_shaext
	.rva	.LSEH_end_${func}_shaext
	.rva	.LSEH_info_${func}_shaext
___
$code.=<<___ if ($SZ==4);
	.rva	.LSEH_begin_${func}_ssse3
	.rva	.LSEH_end_${func}_ssse3
	.rva	.LSEH_info_${func}_ssse3
___
$code.=<<___ if ($avx);
	.rva	.LSEH_begin_${func}_avx
	.rva	.LSEH_end_${func}_avx
	.rva	.LSEH_info_${func}_avx
___
$code.=<<___ if ($avx>1);
	.rva	.LSEH_begin_${func}_avx2
	.rva	.LSEH_end_${func}_avx2
	.rva	.LSEH_info_${func}_avx2
___
$code.=<<___;
.section	.xdata
.align	8
.LSEH_info_$func:
	.byte	9,0,0,0
	.rva	se_handler
	.rva	.Lprologue,.Lepilogue			# HandlerData[]
___
$code.=<<___ if ($SZ==4 && $shaext);
.LSEH_info_${func}_shaext:
	.byte	9,0,0,0
	.rva	shaext_handler
___
$code.=<<___ if ($SZ==4);
.LSEH_info_${func}_ssse3:
	.byte	9,0,0,0
	.rva	se_handler
	.rva	.Lprologue_ssse3,.Lepilogue_ssse3	# HandlerData[]
___
$code.=<<___ if ($avx);
.LSEH_info_${func}_avx:
	.byte	9,0,0,0
	.rva	se_handler
	.rva	.Lprologue_avx,.Lepilogue_avx		# HandlerData[]
___
$code.=<<___ if ($avx>1);
.LSEH_info_${func}_avx2:
	.byte	9,0,0,0
	.rva	se_handler
	.rva	.Lprologue_avx2,.Lepilogue_avx2		# HandlerData[]
___
}

sub sha256op38 {
    my $instr = shift;
    my %opcodelet = (
		"sha256rnds2" => 0xcb,
  		"sha256msg1"  => 0xcc,
		"sha256msg2"  => 0xcd	);

    if (defined($opcodelet{$instr}) && @_[0] =~ /%xmm([0-7]),\s*%xmm([0-7])/) {
      my @opcode=(0x0f,0x38);
	push @opcode,$opcodelet{$instr};
	push @opcode,0xc0|($1&7)|(($2&7)<<3);		# ModR/M
	return ".byte\t".join(',',@opcode);
    } else {
	return $instr."\t".@_[0];
    }
}

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

	s/\b(sha256[^\s]*)\s+(.*)/sha256op38($1,$2)/geo;

	print $_,"\n";
}
close STDOUT or die "error closing STDOUT: $!";


#! /usr/bin/env perl
# Copyright 2015-2016 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

use strict;

my $flavour = shift;
my $output = shift;
open STDOUT,">$output" || die "can't open $output: $!";

$flavour = "linux32" if (!$flavour or $flavour eq "void");

my %GLOBALS;
my $dotinlocallabels=($flavour=~/linux/)?1:0;

################################################################
# directives which need special treatment on different platforms
################################################################
my $arch = sub {
    if ($flavour =~ /linux/)	{ ".arch\t".join(',',@_); }
    elsif ($flavour =~ /win64/) { ".arch\t".join(',',@_); }
    else			{ ""; }
};
my $fpu = sub {
    if ($flavour =~ /linux/)	{ ".fpu\t".join(',',@_); }
    else			{ ""; }
};
my $hidden = sub {
    if ($flavour =~ /ios/)	{ ".private_extern\t".join(',',@_); }
    elsif ($flavour =~ /win64/) { ""; }
    else			{ ".hidden\t".join(',',@_); }
};
my $comm = sub {
    my @args = split(/,\s*/,shift);
    my $name = @args[0];
    my $global = \$GLOBALS{$name};
    my $ret;

    if ($flavour =~ /ios32/)	{
	$ret = ".comm\t_$name,@args[1]\n";
	$ret .= ".non_lazy_symbol_pointer\n";
	$ret .= "$name:\n";
	$ret .= ".indirect_symbol\t_$name\n";
	$ret .= ".long\t0";
	$name = "_$name";
    } else			{ $ret = ".comm\t".join(',',@args); }

    $$global = $name;
    $ret;
};
my $globl = sub {
    my $name = shift;
    my $global = \$GLOBALS{$name};
    my $ret;

    SWITCH: for ($flavour) {
	/ios/		&& do { $name = "_$name";
				last;
			      };
    }

    $ret = ".globl	$name\n";
    # All symbols in assembly files are hidden.
    $ret .= &$hidden($name);
    $$global = $name;
    $ret;
};
my $global = $globl;
my $extern = sub {
    &$globl(@_);
    return;	# return nothing
};
my $type = sub {
    if ($flavour =~ /linux/)	{ ".type\t".join(',',@_); }
    elsif ($flavour =~ /ios32/)	{ if (join(',',@_) =~ /(\w+),%function/) {
					"#ifdef __thumb2__\n".
					".thumb_func	$1\n".
					"#endif";
				  }
			        }
    elsif ($flavour =~ /win64/) { if (join(',',@_) =~ /(\w+),%function/) {
                # See https://sourceware.org/binutils/docs/as/Pseudo-Ops.html
                # Per https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-symbol-table,
                # the type for functions is 0x20, or 32.
                ".def $1\n".
                "   .type 32\n".
                ".endef";
            }
        }
    else			{ ""; }
};
my $size = sub {
    if ($flavour =~ /linux/)	{ ".size\t".join(',',@_); }
    else			{ ""; }
};
my $inst = sub {
    if ($flavour =~ /linux/)    { ".inst\t".join(',',@_); }
    else                        { ".long\t".join(',',@_); }
};
my $asciz = sub {
    my $line = join(",",@_);
    if ($line =~ /^"(.*)"$/)
    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
    else
    {	"";	}
};
my $section = sub {
    if ($flavour =~ /ios/) {
        if ($_[0] eq ".rodata") {
            return ".section\t__TEXT,__const";
        }
        die "Unknown section name $_[0]";
    } else {
        return ".section\t" . join(",", @_);
    }
};

sub range {
  my ($r,$sfx,$start,$end) = @_;

    join(",",map("$r$_$sfx",($start..$end)));
}

sub expand_line {
  my $line = shift;
  my @ret = ();

    pos($line)=0;

    while ($line =~ m/\G[^@\/\{\"]*/g) {
	if ($line =~ m/\G(@|\/\/|$)/gc) {
	    last;
	}
	elsif ($line =~ m/\G\{/gc) {
	    my $saved_pos = pos($line);
	    $line =~ s/\G([rdqv])([0-9]+)([^\-]*)\-\1([0-9]+)\3/range($1,$3,$2,$4)/e;
	    pos($line) = $saved_pos;
	    $line =~ m/\G[^\}]*\}/g;
	}
	elsif ($line =~ m/\G\"/gc) {
	    $line =~ m/\G[^\"]*\"/g;
	}
    }

    $line =~ s/\b(\w+)/$GLOBALS{$1} or $1/ge;

    return $line;
}

my ($arch_defines, $target_defines);
if ($flavour =~ /32/) {
    $arch_defines = "defined(__ARMEL__)";
} elsif ($flavour =~ /64/) {
    $arch_defines = "defined(__AARCH64EL__)";
} else {
    die "unknown architecture: $flavour";
}
if ($flavour =~ /linux/) {
    # Although the flavour is specified as "linux", it is really used by all
    # ELF platforms.
    $target_defines = "defined(__ELF__)";
} elsif ($flavour =~ /ios/) {
    # Although the flavour is specified as "ios", it is really used by all Apple
    # platforms.
    $target_defines = "defined(__APPLE__)";
} elsif ($flavour =~ /win/) {
    $target_defines = "defined(_WIN32)";
} else {
    die "unknown target: $flavour";
}

print <<___;
// This file is generated from a similarly-named Perl script in the BoringSSL
// source tree. Do not edit by hand.

#if !defined(__has_feature)
#define __has_feature(x) 0
#endif
#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
#define OPENSSL_NO_ASM
#endif

#if !defined(OPENSSL_NO_ASM) && $arch_defines && $target_defines
___

print "#if defined(BORINGSSL_PREFIX)\n";
print "#include <boringssl_prefix_symbols_asm.h>\n";
print "#endif\n";

while(my $line=<>) {

    if ($line =~ m/^\s*(#|@|\/\/)/)	{ print $line; next; }

    $line =~ s|/\*.*\*/||;	# get rid of C-style comments...
    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
    $line =~ s|\s+$||;		# ... and at the end

    if ($flavour =~ /64/) {
	my $copy = $line;
	# Also remove line comments.
	$copy =~ s|//.*||;
	if ($copy =~ /\b[wx]18\b/) {
	    die "r18 is reserved by the platform and may not be used.";
	}
    }

    {
	$line =~ s|[\b\.]L(\w{2,})|L$1|g;	# common denominator for Locallabel
	$line =~ s|\bL(\w{2,})|\.L$1|g	if ($dotinlocallabels);
    }

    {
	$line =~ s|(^[\.\w]+)\:\s*||;
	my $label = $1;
	if ($label) {
	    printf "%s:",($GLOBALS{$label} or $label);
	}
    }

    if ($line !~ m/^[#@]/) {
	$line =~ s|^\s*(\.?)(\S+)\s*||;
	my $c = $1; $c = "\t" if ($c eq "");
	my $mnemonic = $2;
	my $opcode;
	if ($mnemonic =~ m/([^\.]+)\.([^\.]+)/) {
	    $opcode = eval("\$$1_$2");
	} else {
	    $opcode = eval("\$$mnemonic");
	}

	if ($flavour =~ /ios/) {
	    # Mach-O and ELF use different syntax for these relocations. Note
	    # that we require :pg_hi21: to be explicitly listed. It is normally
	    # optional with adrp instructions.
	    $line =~ s|:pg_hi21:(\w+)|\1\@PAGE|;
	    $line =~ s|:lo12:(\w+)|\1\@PAGEOFF|;
	} else {
	    # Clang's integrated assembly does not support the optional
	    # :pg_hi21: markers, so erase them.
	    $line =~ s|:pg_hi21:||;
	}

	my $arg=expand_line($line);

	if (ref($opcode) eq 'CODE') {
		$line = &$opcode($arg);
	} elsif ($mnemonic)         {
		$line = $c.$mnemonic;
		$line.= "\t$arg" if ($arg ne "");
	}
    }

    print $line if ($line);
    print "\n";
}

print <<___;
#endif  // !OPENSSL_NO_ASM && $arch_defines && $target_defines
#if defined(__ELF__)
// See https://www.airs.com/blog/archives/518.
.section .note.GNU-stack,"",\%progbits
#endif
___

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


#! /usr/bin/env perl
# Copyright 2006-2016 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

my $flavour = shift;
my $output = shift;
open STDOUT,">$output" || die "can't open $output: $!";

my %GLOBALS;
my %TYPES;
my $dotinlocallabels=($flavour=~/linux/)?1:0;

################################################################
# directives which need special treatment on different platforms
################################################################
my $type = sub {
    my ($dir,$name,$type) = @_;

    $TYPES{$name} = $type;
    if ($flavour =~ /linux/) {
	$name =~ s|^\.||;
	".type	$name,$type";
    } else {
	"";
    }
};
my $globl = sub {
    my $junk = shift;
    my $name = shift;
    my $global = \$GLOBALS{$name};
    my $type = \$TYPES{$name};
    my $ret;

    $name =~ s|^\.||;

    SWITCH: for ($flavour) {
	/aix/		&& do { if (!$$type) {
				    $$type = "\@function";
				}
				if ($$type =~ /function/) {
				    $name = ".$name";
				}
				last;
			      };
	/osx/		&& do { $name = "_$name";
				last;
			      };
	/linux.*(32|64le)/
			&& do {	$ret .= ".globl	$name";
				if (!$$type) {
				    $ret .= "\n.type	$name,\@function";
				    $$type = "\@function";
				}
				last;
			      };
	/linux.*64/	&& do {	$ret .= ".globl	$name";
				if (!$$type) {
				    $ret .= "\n.type	$name,\@function";
				    $$type = "\@function";
				}
				if ($$type =~ /function/) {
				    $ret .= "\n.section	\".opd\",\"aw\"";
				    $ret .= "\n.align	3";
				    $ret .= "\n$name:";
				    $ret .= "\n.quad	.$name,.TOC.\@tocbase,0";
				    $ret .= "\n.previous";
				    $name = ".$name";
				}
				last;
			      };
    }

    $ret = ".globl	$name" if (!$ret);
    $$global = $name;
    $ret;
};
my $text = sub {
    my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text";
    $ret = ".abiversion	2\n".$ret	if ($flavour =~ /linux.*64le/);
    $ret;
};
my $machine = sub {
    my $junk = shift;
    my $arch = shift;
    if ($flavour =~ /osx/)
    {	$arch =~ s/\"//g;
	$arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any");
    }
    ".machine	$arch";
};
my $size = sub {
    if ($flavour =~ /linux/)
    {	shift;
	my $name = shift;
	my $real = $GLOBALS{$name} ? \$GLOBALS{$name} : \$name;
	my $ret  = ".size	$$real,.-$$real";
	$name =~ s|^\.||;
	if ($$real ne $name) {
	    $ret .= "\n.size	$name,.-$$real";
	}
	$ret;
    }
    else
    {	"";	}
};
my $asciz = sub {
    shift;
    my $line = join(",",@_);
    if ($line =~ /^"(.*)"$/)
    {	".byte	" . join(",",unpack("C*",$1),0) . "\n.align	2";	}
    else
    {	"";	}
};
my $quad = sub {
    shift;
    my @ret;
    my ($hi,$lo);
    for (@_) {
	if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io)
	{  $hi=$1?"0x$1":"0"; $lo="0x$2";  }
	elsif (/^([0-9]+)$/o)
	{  $hi=$1>>32; $lo=$1&0xffffffff;  } # error-prone with 32-bit perl
	else
	{  $hi=undef; $lo=$_; }

	if (defined($hi))
	{  push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo");  }
	else
	{  push(@ret,".quad	$lo");  }
    }
    join("\n",@ret);
};

################################################################
# simplified mnemonics not handled by at least one assembler
################################################################
my $cmplw = sub {
    my $f = shift;
    my $cr = 0; $cr = shift if ($#_>1);
    # Some out-of-date 32-bit GNU assembler just can't handle cmplw...
    ($flavour =~ /linux.*32/) ?
	"	.long	".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 :
	"	cmplw	".join(',',$cr,@_);
};
my $bdnz = sub {
    my $f = shift;
    my $bo = $f=~/[\+\-]/ ? 16+9 : 16;	# optional "to be taken" hint
    "	bc	$bo,0,".shift;
} if ($flavour!~/linux/);
my $bltlr = sub {
    my $f = shift;
    my $bo = $f=~/\-/ ? 12+2 : 12;	# optional "not to be taken" hint
    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
	"	.long	".sprintf "0x%x",19<<26|$bo<<21|16<<1 :
	"	bclr	$bo,0";
};
my $bnelr = sub {
    my $f = shift;
    my $bo = $f=~/\-/ ? 4+2 : 4;	# optional "not to be taken" hint
    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
	"	.long	".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 :
	"	bclr	$bo,2";
};
my $beqlr = sub {
    my $f = shift;
    my $bo = $f=~/-/ ? 12+2 : 12;	# optional "not to be taken" hint
    ($flavour =~ /linux/) ?		# GNU as doesn't allow most recent hints
	"	.long	".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 :
	"	bclr	$bo,2";
};
# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two
# arguments is 64, with "operand out of range" error.
my $extrdi = sub {
    my ($f,$ra,$rs,$n,$b) = @_;
    $b = ($b+$n)&63; $n = 64-$n;
    "	rldicl	$ra,$rs,$b,$n";
};
my $vmr = sub {
    my ($f,$vx,$vy) = @_;
    "	vor	$vx,$vy,$vy";
};

# Some ABIs specify vrsave, special-purpose register #256, as reserved
# for system use.
my $no_vrsave = ($flavour =~ /aix|linux64le/);
my $mtspr = sub {
    my ($f,$idx,$ra) = @_;
    if ($idx == 256 && $no_vrsave) {
	"	or	$ra,$ra,$ra";
    } else {
	"	mtspr	$idx,$ra";
    }
};
my $mfspr = sub {
    my ($f,$rd,$idx) = @_;
    if ($idx == 256 && $no_vrsave) {
	"	li	$rd,-1";
    } else {
	"	mfspr	$rd,$idx";
    }
};

# PowerISA 2.06 stuff
sub vsxmem_op {
    my ($f, $vrt, $ra, $rb, $op) = @_;
    "	.long	".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1);
}
# made-up unaligned memory reference AltiVec/VMX instructions
my $lvx_u	= sub {	vsxmem_op(@_, 844); };	# lxvd2x
my $stvx_u	= sub {	vsxmem_op(@_, 972); };	# stxvd2x
my $lvdx_u	= sub {	vsxmem_op(@_, 588); };	# lxsdx
my $stvdx_u	= sub {	vsxmem_op(@_, 716); };	# stxsdx
my $lvx_4w	= sub { vsxmem_op(@_, 780); };	# lxvw4x
my $stvx_4w	= sub { vsxmem_op(@_, 908); };	# stxvw4x

# PowerISA 2.07 stuff
sub vcrypto_op {
    my ($f, $vrt, $vra, $vrb, $op) = @_;
    "	.long	".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op;
}
my $vcipher	= sub { vcrypto_op(@_, 1288); };
my $vcipherlast	= sub { vcrypto_op(@_, 1289); };
my $vncipher	= sub { vcrypto_op(@_, 1352); };
my $vncipherlast= sub { vcrypto_op(@_, 1353); };
my $vsbox	= sub { vcrypto_op(@_, 0, 1480); };
my $vshasigmad	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); };
my $vshasigmaw	= sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); };
my $vpmsumb	= sub { vcrypto_op(@_, 1032); };
my $vpmsumd	= sub { vcrypto_op(@_, 1224); };
my $vpmsubh	= sub { vcrypto_op(@_, 1096); };
my $vpmsumw	= sub { vcrypto_op(@_, 1160); };
my $vaddudm	= sub { vcrypto_op(@_, 192);  };

my $mtsle	= sub {
    my ($f, $arg) = @_;
    "	.long	".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2);
};

# PowerISA 3.0 stuff
my $maddhdu = sub {
    my ($f, $rt, $ra, $rb, $rc) = @_;
    "	.long	".sprintf "0x%X",(4<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($rc<<6)|49;
};
my $maddld = sub {
    my ($f, $rt, $ra, $rb, $rc) = @_;
    "	.long	".sprintf "0x%X",(4<<26)|($rt<<21)|($ra<<16)|($rb<<11)|($rc<<6)|51;
};

my $darn = sub {
    my ($f, $rt, $l) = @_;
    "	.long	".sprintf "0x%X",(31<<26)|($rt<<21)|($l<<16)|(755<<1);
};

print <<___;
// This file is generated from a similarly-named Perl script in the BoringSSL
// source tree. Do not edit by hand.

#if defined(__has_feature)
#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
#define OPENSSL_NO_ASM
#endif
#endif

#if !defined(OPENSSL_NO_ASM) && defined(__powerpc64__) && defined(__ELF__)
#if defined(BORINGSSL_PREFIX)
#include <boringssl_prefix_symbols_asm.h>
#endif
___

while($line=<>) {

    $line =~ s|[#!;].*$||;	# get rid of asm-style comments...
    $line =~ s|/\*.*\*/||;	# ... and C-style comments...
    $line =~ s|^\s+||;		# ... and skip white spaces in beginning...
    $line =~ s|\s+$||;		# ... and at the end

    {
	$line =~ s|\.L(\w+)|L$1|g;	# common denominator for Locallabel
	$line =~ s|\bL(\w+)|\.L$1|g	if ($dotinlocallabels);
    }

    {
	$line =~ s|(^[\.\w]+)\:\s*||;
	my $label = $1;
	if ($label) {
	    my $xlated = ($GLOBALS{$label} or $label);
	    print "$xlated:";
	    if ($flavour =~ /linux.*64le/) {
		if ($TYPES{$label} =~ /function/) {
		    printf "\n.localentry	%s,0\n",$xlated;
		}
	    }
	}
    }

    {
	$line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||;
	my $c = $1; $c = "\t" if ($c eq "");
	my $mnemonic = $2;
	my $f = $3;
	my $opcode = eval("\$$mnemonic");
	$line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/);
	if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); }
	elsif ($mnemonic)           { $line = $c.$mnemonic.$f."\t".$line; }
    }

    print $line if ($line);
    print "\n";
}

print <<___;
#endif  // !OPENSSL_NO_ASM && __powerpc64__ && __ELF__
#if defined(__ELF__)
// See https://www.airs.com/blog/archives/518.
.section .note.GNU-stack,"",\%progbits
#endif
___

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


#! /usr/bin/env perl
# Copyright 1995-2018 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

# require 'x86asm.pl';
# &asm_init(<flavor>[,$i386only]);
# &function_begin("foo");
# ...
# &function_end("foo");
# &asm_finish

$out=();
$i386=0;

# AUTOLOAD is this context has quite unpleasant side effect, namely
# that typos in function calls effectively go to assembler output,
# but on the pros side we don't have to implement one subroutine per
# each opcode...
sub ::AUTOLOAD
{ my $opcode = $AUTOLOAD;

    die "more than 4 arguments passed to $opcode" if ($#_>3);

    $opcode =~ s/.*:://;
    if    ($opcode =~ /^push/) { $stack+=4; }
    elsif ($opcode =~ /^pop/)  { $stack-=4; }

    &generic($opcode,@_) or die "undefined subroutine \&$AUTOLOAD";
}

# record_function_hit(int) writes a byte with value one to the given offset of
# |BORINGSSL_function_hit|, but only if BORINGSSL_DISPATCH_TEST is defined.
# This is used in impl_dispatch_test.cc to test whether the expected assembly
# functions are triggered by high-level API calls.
sub ::record_function_hit
{ my($index)=@_;
    &preprocessor_ifdef("BORINGSSL_DISPATCH_TEST");
    &push("ebx");
    &push("edx");
    &call(&label("pic"));
    &set_label("pic");
    &blindpop("ebx");
    &lea("ebx",&DWP("BORINGSSL_function_hit+$index"."-".&label("pic"),"ebx"));
    &mov("edx", 1);
    &movb(&BP(0, "ebx"), "dl");
    &pop("edx");
    &pop("ebx");
    &preprocessor_endif();
}

sub ::emit
{ my $opcode=shift;

    if ($#_==-1)    { push(@out,"\t$opcode\n");				}
    else            { push(@out,"\t$opcode\t".join(',',@_)."\n");	}
}

sub ::LB
{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'low byte'";
  $1."l";
}
sub ::HB
{   $_[0] =~ m/^e?([a-d])x$/o or die "$_[0] does not have a 'high byte'";
  $1."h";
}
sub ::stack_push{ my $num=$_[0]*4; $stack+=$num; &sub("esp",$num);	}
sub ::stack_pop	{ my $num=$_[0]*4; $stack-=$num; &add("esp",$num);	}
sub ::blindpop	{ &pop($_[0]); $stack+=4;				}
sub ::wparam	{ &DWP($stack+4*$_[0],"esp");				}
sub ::swtmp	{ &DWP(4*$_[0],"esp");					}

sub ::bswap
{   if ($i386)	# emulate bswap for i386
    {	&comment("bswap @_");
	&xchg(&HB(@_),&LB(@_));
	&ror (@_,16);
	&xchg(&HB(@_),&LB(@_));
    }
    else
    {	&generic("bswap",@_);	}
}
# These are made-up opcodes introduced over the years essentially
# by ignorance, just alias them to real ones...
sub ::movb	{ &mov(@_);	}
sub ::xorb	{ &xor(@_);	}
sub ::rotl	{ &rol(@_);	}
sub ::rotr	{ &ror(@_);	}
sub ::exch	{ &xchg(@_);	}
sub ::halt	{ &hlt;		}
sub ::movz	{ &movzx(@_);	}
sub ::pushf	{ &pushfd;	}
sub ::popf	{ &popfd;	}

# 3 argument instructions
sub ::movq
{ my($p1,$p2,$optimize)=@_;

    if ($optimize && $p1=~/^mm[0-7]$/ && $p2=~/^mm[0-7]$/)
    # movq between mmx registers can sink Intel CPUs
    {	&::pshufw($p1,$p2,0xe4);		}
    else
    {	&::generic("movq",@_);			}
}

# SSE>2 instructions
my %regrm = (	"eax"=>0, "ecx"=>1, "edx"=>2, "ebx"=>3,
		"esp"=>4, "ebp"=>5, "esi"=>6, "edi"=>7	);
sub ::pextrd
{ my($dst,$src,$imm)=@_;
    if ("$dst:$src" =~ /(e[a-dsd][ixp]):xmm([0-7])/)
    {	&::data_byte(0x66,0x0f,0x3a,0x16,0xc0|($2<<3)|$regrm{$1},$imm);	}
    else
    {	&::generic("pextrd",@_);		}
}

sub ::pinsrd
{ my($dst,$src,$imm)=@_;
    if ("$dst:$src" =~ /xmm([0-7]):(e[a-dsd][ixp])/)
    {	&::data_byte(0x66,0x0f,0x3a,0x22,0xc0|($1<<3)|$regrm{$2},$imm);	}
    else
    {	&::generic("pinsrd",@_);		}
}

sub ::pshufb
{ my($dst,$src)=@_;
    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
    {	&data_byte(0x66,0x0f,0x38,0x00,0xc0|($1<<3)|$2);	}
    else
    {	&::generic("pshufb",@_);		}
}

sub ::palignr
{ my($dst,$src,$imm)=@_;
    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
    {	&::data_byte(0x66,0x0f,0x3a,0x0f,0xc0|($1<<3)|$2,$imm);	}
    else
    {	&::generic("palignr",@_);		}
}

sub ::pclmulqdq
{ my($dst,$src,$imm)=@_;
    if ("$dst:$src" =~ /xmm([0-7]):xmm([0-7])/)
    {	&::data_byte(0x66,0x0f,0x3a,0x44,0xc0|($1<<3)|$2,$imm);	}
    else
    {	&::generic("pclmulqdq",@_);		}
}

sub ::rdrand
{ my ($dst)=@_;
    if ($dst =~ /(e[a-dsd][ixp])/)
    {	&::data_byte(0x0f,0xc7,0xf0|$regrm{$dst});	}
    else
    {	&::generic("rdrand",@_);	}
}

sub ::rdseed
{ my ($dst)=@_;
    if ($dst =~ /(e[a-dsd][ixp])/)
    {	&::data_byte(0x0f,0xc7,0xf8|$regrm{$dst});	}
    else
    {	&::generic("rdrand",@_);	}
}

sub rxb {
 local *opcode=shift;
 my ($dst,$src1,$src2,$rxb)=@_;

   $rxb|=0x7<<5;
   $rxb&=~(0x04<<5) if($dst>=8);
   $rxb&=~(0x01<<5) if($src1>=8);
   $rxb&=~(0x02<<5) if($src2>=8);
   push @opcode,$rxb;
}

sub ::vprotd
{ my $args=join(',',@_);
    if ($args =~ /xmm([0-7]),xmm([0-7]),([x0-9a-f]+)/)
    { my @opcode=(0x8f);
	rxb(\@opcode,$1,$2,-1,0x08);
	push @opcode,0x78,0xc2;
	push @opcode,0xc0|($2&7)|(($1&7)<<3);		# ModR/M
	my $c=$3;
	push @opcode,$c=~/^0/?oct($c):$c;
	&::data_byte(@opcode);
    }
    else
    {	&::generic("vprotd",@_);	}
}

sub ::endbranch
{
    &::data_byte(0xf3,0x0f,0x1e,0xfb);
}

# label management
$lbdecor="L";		# local label decoration, set by package
$label="000";

sub ::islabel		# see is argument is a known label
{ my $i;
    foreach $i (values %label) { return $i if ($i eq $_[0]); }
  $label{$_[0]};	# can be undef
}

sub ::label		# instantiate a function-scope label
{   if (!defined($label{$_[0]}))
    {	$label{$_[0]}="${lbdecor}${label}${_[0]}"; $label++;   }
  $label{$_[0]};
}

sub ::LABEL		# instantiate a file-scope label
{   $label{$_[0]}=$_[1] if (!defined($label{$_[0]}));
  $label{$_[0]};
}

sub ::static_label	{ &::LABEL($_[0],$lbdecor.$_[0]); }

sub ::set_label_B	{ push(@out,"@_:\n"); }
sub ::set_label
{ my $label=&::label($_[0]);
    &::align($_[1]) if ($_[1]>1);
    &::set_label_B($label);
  $label;
}

sub ::wipe_labels	# wipes function-scope labels
{   foreach $i (keys %label)
    {	delete $label{$i} if ($label{$i} =~ /^\Q${lbdecor}\E[0-9]{3}/);	}
}

# subroutine management
sub ::function_begin
{   &function_begin_B(@_);
    $stack=4;
    &push("ebp");
    &push("ebx");
    &push("esi");
    &push("edi");
}

sub ::function_end
{   &pop("edi");
    &pop("esi");
    &pop("ebx");
    &pop("ebp");
    &ret();
    &function_end_B(@_);
    $stack=0;
    &wipe_labels();
}

sub ::function_end_A
{   &pop("edi");
    &pop("esi");
    &pop("ebx");
    &pop("ebp");
    &ret();
    $stack+=16;	# readjust esp as if we didn't pop anything
}

sub ::asciz
{ my @str=unpack("C*",shift);
    push @str,0;
    while ($#str>15) {
	&data_byte(@str[0..15]);
	foreach (0..15) { shift @str; }
    }
    &data_byte(@str) if (@str);
}

sub ::asm_finish
{   &file_end();
    my $comment = "//";
    $comment = ";" if ($win32);
    print <<___;
$comment This file is generated from a similarly-named Perl script in the BoringSSL
$comment source tree. Do not edit by hand.

___
    if ($win32) {
        print <<___ unless $masm;
\%ifdef BORINGSSL_PREFIX
\%include "boringssl_prefix_symbols_nasm.inc"
\%endif
\%ifidn __OUTPUT_FORMAT__, win32
___
        print @out;
        print <<___ unless $masm;
\%else
; Work around https://bugzilla.nasm.us/show_bug.cgi?id=3392738
ret
\%endif
___
    } else {
        my $target;
        if ($elf) {
            $target = "defined(__ELF__)";
        } elsif ($macosx) {
            $target = "defined(__APPLE__)";
        } else {
            die "unknown target";
        }

        print <<___;
#if defined(__has_feature)
#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
#define OPENSSL_NO_ASM
#endif
#endif

#if !defined(OPENSSL_NO_ASM) && defined(__i386__) && $target
#if defined(BORINGSSL_PREFIX)
#include <boringssl_prefix_symbols_asm.h>
#endif
___
        print @out;
        print <<___;
#endif  // !defined(OPENSSL_NO_ASM) && defined(__i386__) && $target
#if defined(__ELF__)
// See https://www.airs.com/blog/archives/518.
.section .note.GNU-stack,"",\%progbits
#endif
___
    }
}

sub ::asm_init
{ my ($type,$cpu)=@_;

    $i386=$cpu;

    $elf=$cpp=$coff=$aout=$macosx=$win32=$mwerks=$android=0;
    if    (($type eq "elf"))
    {	$elf=1;			require "x86gas.pl";	}
    elsif (($type eq "elf-1"))
    {	$elf=-1;		require "x86gas.pl";	}
    elsif (($type eq "a\.out"))
    {	$aout=1;		require "x86gas.pl";	}
    elsif (($type eq "coff" or $type eq "gaswin"))
    {	$coff=1;		require "x86gas.pl";	}
    elsif (($type eq "win32n"))
    {	$win32=1;		require "x86nasm.pl";	}
    elsif (($type eq "win32"))
    {	$win32=1; $masm=1;	require "x86masm.pl";	}
    elsif (($type eq "macosx"))
    {	$aout=1; $macosx=1;	require "x86gas.pl";	}
    elsif (($type eq "android"))
    {	$elf=1; $android=1;	require "x86gas.pl";	}
    else
    {	print STDERR <<"EOF";
Pick one target type from
	elf	- Linux, FreeBSD, Solaris x86, etc.
	a.out	- DJGPP, elder OpenBSD, etc.
	coff	- GAS/COFF such as Win32 targets
	win32n	- Windows 95/Windows NT NASM format
	macosx	- Mac OS X
EOF
	exit(1);
    }

    $pic=0;
    for (@ARGV) { $pic=1 if (/\-[fK]PIC/i); }

    &file();
}

sub ::hidden {}

1;


#! /usr/bin/env perl
# Copyright 2007-2016 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

package x86gas;

*out=\@::out;

$::lbdecor=$::aout?"L":".L";		# local label decoration
$nmdecor=($::aout or $::coff)?"_":"";	# external name decoration

$initseg="";

$align=16;
$align=log($align)/log(2) if ($::aout);
$com_start="#" if ($::aout or $::coff);

sub opsize()
{ my $reg=shift;
    if    ($reg =~ m/^%e/o)		{ "l"; }
    elsif ($reg =~ m/^%[a-d][hl]$/o)	{ "b"; }
    elsif ($reg =~ m/^%[yxm]/o)		{ undef; }
    else				{ "w"; }
}

# swap arguments;
# expand opcode with size suffix;
# prefix numeric constants with $;
sub ::generic
{ my($opcode,@arg)=@_;
  my($suffix,$dst,$src);

    @arg=reverse(@arg);

    for (@arg)
    {	s/^(\*?)(e?[a-dsixphl]{2})$/$1%$2/o;	# gp registers
	s/^([xy]?mm[0-7])$/%$1/o;		# xmm/mmx registers
	s/^(\-?[0-9]+)$/\$$1/o;			# constants
	s/^(\-?0x[0-9a-f]+)$/\$$1/o;		# constants
    }

    $dst = $arg[$#arg]		if ($#arg>=0);
    $src = $arg[$#arg-1]	if ($#arg>=1);
    if    ($dst =~ m/^%/o)	{ $suffix=&opsize($dst); }
    elsif ($src =~ m/^%/o)	{ $suffix=&opsize($src); }
    else			{ $suffix="l";           }
    undef $suffix if ($dst =~ m/^%[xm]/o || $src =~ m/^%[xm]/o);

    if ($#_==0)				{ &::emit($opcode);		}
    elsif ($#_==1 && $opcode =~ m/^(call|clflush|j|loop|set)/o)
					{ &::emit($opcode,@arg);	}
    else				{ &::emit($opcode.$suffix,@arg);}

  1;
}
#
# opcodes not covered by ::generic above, mostly inconsistent namings...
#
sub ::movzx	{ &::movzb(@_);			}
sub ::pushfd	{ &::pushfl;			}
sub ::popfd	{ &::popfl;			}
sub ::cpuid	{ &::emit(".byte\t0x0f,0xa2");	}
sub ::rdtsc	{ &::emit(".byte\t0x0f,0x31");	}

sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
sub ::call_ptr	{ &::generic("call","*$_[0]");	}
sub ::jmp_ptr	{ &::generic("jmp","*$_[0]");	}

*::bswap = sub	{ &::emit("bswap","%$_[0]");	} if (!$::i386);

sub ::DWP
{ my($addr,$reg1,$reg2,$idx)=@_;
  my $ret="";

    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }

    $addr =~ s/^\s+//;
    # prepend global references with optional underscore
    $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;

    $reg1 = "%$reg1" if ($reg1);
    $reg2 = "%$reg2" if ($reg2);

    $ret .= $addr if (($addr ne "") && ($addr ne 0));

    if ($reg2)
    {	$idx!= 0 or $idx=1;
	$ret .= "($reg1,$reg2,$idx)";
    }
    elsif ($reg1)
    {	$ret .= "($reg1)";	}

  $ret;
}
sub ::QWP	{ &::DWP(@_);	}
sub ::BP	{ &::DWP(@_);	}
sub ::WP	{ &::DWP(@_);	}
sub ::BC	{ @_;		}
sub ::DWC	{ @_;		}

sub ::file
{   push(@out,".text\n");	}

sub ::function_begin_B
{ my $func=shift;
  my $global=($func !~ /^_/);
  my $begin="${::lbdecor}_${func}_begin";

    &::LABEL($func,$global?"$begin":"$nmdecor$func");
    $func=$nmdecor.$func;

    push(@out,".globl\t$func\n")	if ($global);
    if ($::macosx) {
      push(@out,".private_extern\t$func\n");
    } else {
      push(@out,".hidden\t$func\n");
    }
    if ($::coff)
    {	push(@out,".def\t$func;\t.scl\t".(3-$global).";\t.type\t32;\t.endef\n"); }
    elsif (($::aout and !$::pic) or $::macosx)
    { }
    else
    {	push(@out,".type	$func,\@function\n"); }
    push(@out,".align\t$align\n");
    push(@out,"$func:\n");
    push(@out,"$begin:\n")		if ($global);
    $::stack=4;
}

sub ::function_end_B
{ my $func=shift;
    push(@out,".size\t$nmdecor$func,.-".&::LABEL($func)."\n") if ($::elf);
    $::stack=0;
    &::wipe_labels();
}

sub ::comment
	{
	if (!defined($com_start) or $::elf)
		{	# Regarding $::elf above...
			# GNU and SVR4 as'es use different comment delimiters,
		push(@out,"\n");	# so we just skip ELF comments...
		return;
		}
	foreach (@_)
		{
		if (/^\s*$/)
			{ push(@out,"\n"); }
		else
			{ push(@out,"\t$com_start $_ $com_end\n"); }
		}
	}

sub ::external_label
{   foreach(@_) { &::LABEL($_,$nmdecor.$_); }   }

sub ::public_label
{   push(@out,".globl\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }

sub ::file_end
{   if ($::macosx)
    {	if (%non_lazy_ptr)
    	{   push(@out,".section __IMPORT,__pointers,non_lazy_symbol_pointers\n");
	    foreach $i (keys %non_lazy_ptr)
	    {	push(@out,"$non_lazy_ptr{$i}:\n.indirect_symbol\t$i\n.long\t0\n");   }
	}
    }
    if (0 && grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out) {
	my $tmp=".comm\t${nmdecor}OPENSSL_ia32cap_P,16";
	if ($::macosx)	{ push (@out,"$tmp,2\n"); }
	elsif ($::elf)	{ push (@out,"$tmp,4\n"); }
	else		{ push (@out,"$tmp\n"); }
    }
    push(@out,$initseg) if ($initseg);
}

sub ::data_byte	{   push(@out,".byte\t".join(',',@_)."\n");   }
sub ::data_short{   push(@out,".value\t".join(',',@_)."\n");  }
sub ::data_word {   push(@out,".long\t".join(',',@_)."\n");   }

sub ::align
{ my $val=$_[0];
    if ($::aout)
    {	$val=int(log($val)/log(2));
	$val.=",0x90";
    }
    push(@out,".align\t$val\n");
}

sub ::picmeup
{ my($dst,$sym,$base,$reflabel)=@_;

    if (($::pic && ($::elf || $::aout)) || $::macosx)
    {	if (!defined($base))
	{   &::call(&::label("PIC_me_up"));
	    &::set_label("PIC_me_up");
	    &::blindpop($dst);
	    $base=$dst;
	    $reflabel=&::label("PIC_me_up");
	}
	if ($::macosx)
	{   my $indirect=&::static_label("$nmdecor$sym\$non_lazy_ptr");
	    &::mov($dst,&::DWP("$indirect-$reflabel",$base));
	    $non_lazy_ptr{"$nmdecor$sym"}=$indirect;
	}
	elsif ($sym eq "OPENSSL_ia32cap_P" && $::elf>0)
	{   &::lea($dst,&::DWP("$sym-$reflabel",$base));   }
	else
	{   &::lea($dst,&::DWP("_GLOBAL_OFFSET_TABLE_+[.-$reflabel]",
			    $base));
	    &::mov($dst,&::DWP("$sym\@GOT",$dst));
	}
    }
    else
    {	&::lea($dst,&::DWP($sym));	}
}

sub ::initseg
{ my $f=$nmdecor.shift;

    if ($::android)
    {	$initseg.=<<___;
.section	.init_array
.align	4
.long	$f
___
    }
    elsif ($::elf)
    {	$initseg.=<<___;
.section	.init
	call	$f
___
    }
    elsif ($::coff)
    {   $initseg.=<<___;	# applies to both Cygwin and Mingw
.section	.ctors
.long	$f
___
    }
    elsif ($::macosx)
    {	$initseg.=<<___;
.mod_init_func
.align 2
.long   $f
___
    }
    elsif ($::aout)
    {	my $ctor="${nmdecor}_GLOBAL_\$I\$$f";
	$initseg.=".text\n";
	$initseg.=".type	$ctor,\@function\n" if ($::pic);
	$initseg.=<<___;	# OpenBSD way...
.globl	$ctor
.align	2
$ctor:
	jmp	$f
___
    }
}

sub ::dataseg
{   push(@out,".data\n");   }

sub ::preprocessor_ifdef
{ my($define)=@_;
    push(@out,"#ifdef ${define}\n");
}

sub ::preprocessor_endif
{ push(@out,"#endif\n");    }

*::hidden = sub { push(@out,".hidden\t$nmdecor$_[0]\n"); } if ($::elf);

1;


#! /usr/bin/env perl
# Copyright 2007-2016 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

package x86masm;

*out=\@::out;

$::lbdecor="\$L";	# local label decoration
$nmdecor="_";		# external name decoration

$initseg="";
$segment="";

sub ::generic
{ my ($opcode,@arg)=@_;

    # fix hexadecimal constants
    for (@arg) { s/(?<![\w\$\.])0x([0-9a-f]+)/0$1h/oi; }

    if ($opcode =~ /lea/ && @arg[1] =~ s/.*PTR\s+(\(.*\))$/OFFSET $1/)	# no []
    {	$opcode="mov";	}
    elsif ($opcode !~ /mov[dq]$/)
    {	# fix xmm references
	$arg[0] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[-1]=~/\bxmm[0-7]\b/i);
	$arg[-1] =~ s/\b[A-Z]+WORD\s+PTR/XMMWORD PTR/i if ($arg[0]=~/\bxmm[0-7]\b/i);
    }

    &::emit($opcode,@arg);
  1;
}
#
# opcodes not covered by ::generic above, mostly inconsistent namings...
#
sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
sub ::call_ptr	{ &::emit("call",@_);	}
sub ::jmp_ptr	{ &::emit("jmp",@_);	}
sub ::lock	{ &::data_byte(0xf0);	}

sub get_mem
{ my($size,$addr,$reg1,$reg2,$idx)=@_;
  my($post,$ret);

    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }

    $ret .= "$size PTR " if ($size ne "");

    $addr =~ s/^\s+//;
    # prepend global references with optional underscore
    $addr =~ s/^([^\+\-0-9][^\+\-]*)/&::islabel($1) or "$nmdecor$1"/ige;
    # put address arithmetic expression in parenthesis
    $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);

    if (($addr ne "") && ($addr ne 0))
    {	if ($addr !~ /^-/)	{ $ret .= "$addr";  }
	else			{ $post=$addr;      }
    }
    $ret .= "[";

    if ($reg2 ne "")
    {	$idx!=0 or $idx=1;
	$ret .= "$reg2*$idx";
	$ret .= "+$reg1" if ($reg1 ne "");
    }
    else
    {	$ret .= "$reg1";   }

    $ret .= "$post]";
    $ret =~ s/\+\]/]/; # in case $addr was the only argument
    $ret =~ s/\[\s*\]//;

  $ret;
}
sub ::BP	{ &get_mem("BYTE",@_);  }
sub ::WP	{ &get_mem("WORD",@_);	}
sub ::DWP	{ &get_mem("DWORD",@_); }
sub ::QWP	{ &get_mem("QWORD",@_); }
sub ::BC	{ "@_";  }
sub ::DWC	{ "@_"; }

sub ::file
{ my $tmp=<<___;
IF \@Version LT 800
ECHO MASM version 8.00 or later is strongly recommended.
ENDIF
.686
.MODEL	FLAT
OPTION	DOTNAME
IF \@Version LT 800
.text\$	SEGMENT PAGE 'CODE'
ELSE
.text\$	SEGMENT ALIGN(64) 'CODE'
ENDIF
___
    push(@out,$tmp);
    $segment = ".text\$";
}

sub ::function_begin_B
{ my $func=shift;
  my $global=($func !~ /^_/);
  my $begin="${::lbdecor}_${func}_begin";

    &::LABEL($func,$global?"$begin":"$nmdecor$func");
    $func="ALIGN\t16\n".$nmdecor.$func."\tPROC";

    if ($global)    { $func.=" PUBLIC\n${begin}::\n"; }
    else	    { $func.=" PRIVATE\n";            }
    push(@out,$func);
    $::stack=4;
}
sub ::function_end_B
{ my $func=shift;

    push(@out,"$nmdecor$func ENDP\n");
    $::stack=0;
    &::wipe_labels();
}

sub ::file_end
{ my $xmmheader=<<___;
.686
.XMM
IF \@Version LT 800
XMMWORD STRUCT 16
DQ	2 dup (?)
XMMWORD	ENDS
ENDIF
___
    if (grep {/\b[x]?mm[0-7]\b/i} @out) {
	grep {s/\.[3-7]86/$xmmheader/} @out;
    }

    push(@out,"$segment	ENDS\n");

    if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
    {	my $comm=<<___;
.bss	SEGMENT 'BSS'
COMM	${nmdecor}OPENSSL_ia32cap_P:DWORD:4
.bss	ENDS
___
	# comment out OPENSSL_ia32cap_P declarations
	grep {s/(^EXTERN\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
	push (@out,$comm);
    }
    push (@out,$initseg) if ($initseg);
    push (@out,"END\n");
}

sub ::comment {   foreach (@_) { push(@out,"\t; $_\n"); }   }

*::set_label_B = sub
{ my $l=shift; push(@out,$l.($l=~/^\Q${::lbdecor}\E[0-9]{3}/?":\n":"::\n")); };

sub ::external_label
{   foreach(@_)
    {	push(@out, "EXTERN\t".&::LABEL($_,$nmdecor.$_).":NEAR\n");   }
}

sub ::public_label
{   push(@out,"PUBLIC\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");   }

sub ::data_byte
{   push(@out,("DB\t").join(',',splice(@_,0,16))."\n") while(@_);	}

sub ::data_short
{   push(@out,("DW\t").join(',',splice(@_,0,8))."\n") while(@_);	}

sub ::data_word
{   push(@out,("DD\t").join(',',splice(@_,0,4))."\n") while(@_);	}

sub ::align
{   push(@out,"ALIGN\t$_[0]\n");	}

sub ::picmeup
{ my($dst,$sym)=@_;
    &::lea($dst,&::DWP($sym));
}

sub ::initseg
{ my $f=$nmdecor.shift;

    $initseg.=<<___;
.CRT\$XCU	SEGMENT DWORD PUBLIC 'DATA'
EXTERN	$f:NEAR
DD	$f
.CRT\$XCU	ENDS
___
}

sub ::dataseg
{   push(@out,"$segment\tENDS\n_DATA\tSEGMENT\n"); $segment="_DATA";   }

sub ::safeseh
{ my $nm=shift;
    push(@out,"IF \@Version GE 710\n");
    push(@out,".SAFESEH	".&::LABEL($nm,$nmdecor.$nm)."\n");
    push(@out,"ENDIF\n");
}

sub ::preprocessor_ifdef
{ my($define)=@_;
    push(@out,"%ifdef ${define}\n");
}

sub ::preprocessor_endif
{ push(@out,"%endif\n");    }

1;


#! /usr/bin/env perl
# Copyright 1999-2018 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

package x86nasm;

*out=\@::out;

$::lbdecor="L\$";		# local label decoration
$nmdecor="_";			# external name decoration
$drdecor=$::mwerks?".":"";	# directive decoration

$initseg="";

sub ::generic
{ my $opcode=shift;
  my $tmp;

    if (!$::mwerks)
    {   if    ($opcode =~ m/^j/o && $#_==0) # optimize jumps
	{   $_[0] = "NEAR $_[0]";   	}
	elsif ($opcode eq "lea" && $#_==1)  # wipe storage qualifier from lea
	{   $_[1] =~ s/^[^\[]*\[/\[/o;	}
	elsif ($opcode eq "clflush" && $#_==0)
	{   $_[0] =~ s/^[^\[]*\[/\[/o;	}
    }
    &::emit($opcode,@_);
  1;
}
#
# opcodes not covered by ::generic above, mostly inconsistent namings...
#
sub ::call	{ &::emit("call",(&::islabel($_[0]) or "$nmdecor$_[0]")); }
sub ::call_ptr	{ &::emit("call",@_);	}
sub ::jmp_ptr	{ &::emit("jmp",@_);	}

sub get_mem
{ my($size,$addr,$reg1,$reg2,$idx)=@_;
  my($post,$ret);

    if (!defined($idx) && 1*$reg2) { $idx=$reg2; $reg2=$reg1; undef $reg1; }

    if ($size ne "")
    {	$ret .= "$size";
	$ret .= " PTR" if ($::mwerks);
	$ret .= " ";
    }
    $ret .= "[";

    $addr =~ s/^\s+//;
    # prepend global references with optional underscore
    $addr =~ s/^([^\+\-0-9][^\+\-]*)/::islabel($1) or "$nmdecor$1"/ige;
    # put address arithmetic expression in parenthesis
    $addr="($addr)" if ($addr =~ /^.+[\-\+].+$/);

    if (($addr ne "") && ($addr ne 0))
    {	if ($addr !~ /^-/)	{ $ret .= "$addr+"; }
	else			{ $post=$addr;      }
    }

    if ($reg2 ne "")
    {	$idx!=0 or $idx=1;
	$ret .= "$reg2*$idx";
	$ret .= "+$reg1" if ($reg1 ne "");
    }
    else
    {	$ret .= "$reg1";   }

    $ret .= "$post]";
    $ret =~ s/\+\]/]/; # in case $addr was the only argument

  $ret;
}
sub ::BP	{ &get_mem("BYTE",@_);  }
sub ::DWP	{ &get_mem("DWORD",@_); }
sub ::WP	{ &get_mem("WORD",@_);	}
sub ::QWP	{ &get_mem("",@_);      }
sub ::BC	{ (($::mwerks)?"":"BYTE ")."@_";  }
sub ::DWC	{ (($::mwerks)?"":"DWORD ")."@_"; }

sub ::file
{   if ($::mwerks)	{ push(@out,".section\t.text,64\n"); }
    else
    { my $tmp=<<___;
%ifidn __OUTPUT_FORMAT__,obj
section	code	use32 class=code align=64
%elifidn __OUTPUT_FORMAT__,win32
\$\@feat.00 equ 1
section	.text	code align=64
%else
section	.text	code
%endif
___
	push(@out,$tmp);
    }
}

sub ::function_begin_B
{ my $func=shift;
  my $global=($func !~ /^_/);
  my $begin="${::lbdecor}_${func}_begin";

    $begin =~ s/^\@/./ if ($::mwerks);	# the torture never stops

    &::LABEL($func,$global?"$begin":"$nmdecor$func");
    $func=$nmdecor.$func;

    push(@out,"${drdecor}global	$func\n")	if ($global);
    push(@out,"${drdecor}align	16\n");
    push(@out,"$func:\n");
    push(@out,"$begin:\n")			if ($global);
    $::stack=4;
}

sub ::function_end_B
{   $::stack=0;
    &::wipe_labels();
}

sub ::file_end
{   if (grep {/\b${nmdecor}OPENSSL_ia32cap_P\b/i} @out)
    {	my $comm=<<___;
${drdecor}segment	.bss
${drdecor}common	${nmdecor}OPENSSL_ia32cap_P 16
___
	# comment out OPENSSL_ia32cap_P declarations
	grep {s/(^extern\s+${nmdecor}OPENSSL_ia32cap_P)/\;$1/} @out;
	push (@out,$comm)
    }
    push (@out,$initseg) if ($initseg);
}

sub ::comment {   foreach (@_) { push(@out,"\t; $_\n"); }   }

sub ::external_label
{   foreach(@_)
    {	push(@out,"${drdecor}extern\t".&::LABEL($_,$nmdecor.$_)."\n");   }
}

sub ::public_label
{   push(@out,"${drdecor}global\t".&::LABEL($_[0],$nmdecor.$_[0])."\n");  }

sub ::data_byte
{   push(@out,(($::mwerks)?".byte\t":"db\t").join(',',@_)."\n");	}
sub ::data_short
{   push(@out,(($::mwerks)?".word\t":"dw\t").join(',',@_)."\n");	}
sub ::data_word
{   push(@out,(($::mwerks)?".long\t":"dd\t").join(',',@_)."\n");	}

sub ::align
{   push(@out,"${drdecor}align\t$_[0]\n");	}

sub ::picmeup
{ my($dst,$sym)=@_;
    &::lea($dst,&::DWP($sym));
}

sub ::initseg
{ my $f=$nmdecor.shift;
    if ($::win32)
    {	$initseg=<<___;
segment	.CRT\$XCU data align=4
extern	$f
dd	$f
___
    }
}

sub ::dataseg
{   if ($mwerks)	{ push(@out,".section\t.data,4\n");   }
    else		{ push(@out,"section\t.data align=4\n"); }
}

sub ::safeseh
{ my $nm=shift;
    push(@out,"%if	__NASM_VERSION_ID__ >= 0x02030000\n");
    push(@out,"safeseh	".&::LABEL($nm,$nmdecor.$nm)."\n");
    push(@out,"%endif\n");
}

sub ::preprocessor_ifdef
{ my($define)=@_;
    push(@out,"%ifdef ${define}\n");
}

sub ::preprocessor_endif
{ push(@out,"%endif\n");    }

1;