Blame crypto/bn/asm/armv8-mont.pl

Packit c4476c
#! /usr/bin/env perl
Packit c4476c
# Copyright 2015-2020 The OpenSSL Project Authors. All Rights Reserved.
Packit c4476c
#
Packit c4476c
# Licensed under the OpenSSL license (the "License").  You may not use
Packit c4476c
# this file except in compliance with the License.  You can obtain a copy
Packit c4476c
# in the file LICENSE in the source distribution or at
Packit c4476c
# https://www.openssl.org/source/license.html
Packit c4476c
Packit c4476c
Packit c4476c
# ====================================================================
Packit c4476c
# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
Packit c4476c
# project. The module is, however, dual licensed under OpenSSL and
Packit c4476c
# CRYPTOGAMS licenses depending on where you obtain it. For further
Packit c4476c
# details see http://www.openssl.org/~appro/cryptogams/.
Packit c4476c
# ====================================================================
Packit c4476c
Packit c4476c
# March 2015
Packit c4476c
#
Packit c4476c
# "Teaser" Montgomery multiplication module for ARMv8. Needs more
Packit c4476c
# work. While it does improve RSA sign performance by 20-30% (less for
Packit c4476c
# longer keys) on most processors, for some reason RSA2048 is not
Packit c4476c
# faster and RSA4096 goes 15-20% slower on Cortex-A57. Multiplication
Packit c4476c
# instruction issue rate is limited on processor in question, meaning
Packit c4476c
# that dedicated squaring procedure is a must. Well, actually all
Packit c4476c
# contemporary AArch64 processors seem to have limited multiplication
Packit c4476c
# issue rate, i.e. they can't issue multiplication every cycle, which
Packit c4476c
# explains moderate improvement coefficients in comparison to
Packit c4476c
# compiler-generated code. Recall that compiler is instructed to use
Packit c4476c
# umulh and therefore uses same amount of multiplication instructions
Packit c4476c
# to do the job. Assembly's edge is to minimize number of "collateral"
Packit c4476c
# instructions and of course instruction scheduling.
Packit c4476c
#
Packit c4476c
# April 2015
Packit c4476c
#
Packit c4476c
# Squaring procedure that handles lengths divisible by 8 improves
Packit c4476c
# RSA/DSA performance by 25-40-60% depending on processor and key
Packit c4476c
# length. Overall improvement coefficients are always positive in
Packit c4476c
# comparison to compiler-generated code. On Cortex-A57 improvement
Packit c4476c
# is still modest on longest key lengths, while others exhibit e.g.
Packit c4476c
# 50-70% improvement for RSA4096 sign. RSA2048 sign is ~25% faster
Packit c4476c
# on Cortex-A57 and ~60-100% faster on others.
Packit c4476c
Packit c4476c
$flavour = shift;
Packit c4476c
$output  = shift;
Packit c4476c
Packit c4476c
$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
Packit c4476c
( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
Packit c4476c
( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
Packit c4476c
die "can't locate arm-xlate.pl";
Packit c4476c
Packit c4476c
open OUT,"| \"$^X\" $xlate $flavour $output";
Packit c4476c
*STDOUT=*OUT;
Packit c4476c
Packit c4476c
($lo0,$hi0,$aj,$m0,$alo,$ahi,
Packit c4476c
 $lo1,$hi1,$nj,$m1,$nlo,$nhi,
Packit c4476c
 $ovf, $i,$j,$tp,$tj) = map("x$_",6..17,19..24);
Packit c4476c
Packit c4476c
# int bn_mul_mont(
Packit c4476c
$rp="x0";	# BN_ULONG *rp,
Packit c4476c
$ap="x1";	# const BN_ULONG *ap,
Packit c4476c
$bp="x2";	# const BN_ULONG *bp,
Packit c4476c
$np="x3";	# const BN_ULONG *np,
Packit c4476c
$n0="x4";	# const BN_ULONG *n0,
Packit c4476c
$num="x5";	# int num);
Packit c4476c
Packit c4476c
$code.=<<___;
Packit c4476c
.text
Packit c4476c
Packit c4476c
.globl	bn_mul_mont
Packit c4476c
.type	bn_mul_mont,%function
Packit c4476c
.align	5
Packit c4476c
bn_mul_mont:
Packit c4476c
	tst	$num,#7
Packit c4476c
	b.eq	__bn_sqr8x_mont
Packit c4476c
	tst	$num,#3
Packit c4476c
	b.eq	__bn_mul4x_mont
Packit c4476c
.Lmul_mont:
Packit c4476c
	stp	x29,x30,[sp,#-64]!
Packit c4476c
	add	x29,sp,#0
Packit c4476c
	stp	x19,x20,[sp,#16]
Packit c4476c
	stp	x21,x22,[sp,#32]
Packit c4476c
	stp	x23,x24,[sp,#48]
Packit c4476c
Packit c4476c
	ldr	$m0,[$bp],#8		// bp[0]
Packit c4476c
	sub	$tp,sp,$num,lsl#3
Packit c4476c
	ldp	$hi0,$aj,[$ap],#16	// ap[0..1]
Packit c4476c
	lsl	$num,$num,#3
Packit c4476c
	ldr	$n0,[$n0]		// *n0
Packit c4476c
	and	$tp,$tp,#-16		// ABI says so
Packit c4476c
	ldp	$hi1,$nj,[$np],#16	// np[0..1]
Packit c4476c
Packit c4476c
	mul	$lo0,$hi0,$m0		// ap[0]*bp[0]
Packit c4476c
	sub	$j,$num,#16		// j=num-2
Packit c4476c
	umulh	$hi0,$hi0,$m0
Packit c4476c
	mul	$alo,$aj,$m0		// ap[1]*bp[0]
Packit c4476c
	umulh	$ahi,$aj,$m0
Packit c4476c
Packit c4476c
	mul	$m1,$lo0,$n0		// "tp[0]"*n0
Packit c4476c
	mov	sp,$tp			// alloca
Packit c4476c
Packit c4476c
	// (*)	mul	$lo1,$hi1,$m1	// np[0]*m1
Packit c4476c
	umulh	$hi1,$hi1,$m1
Packit c4476c
	mul	$nlo,$nj,$m1		// np[1]*m1
Packit c4476c
	// (*)	adds	$lo1,$lo1,$lo0	// discarded
Packit c4476c
	// (*)	As for removal of first multiplication and addition
Packit c4476c
	//	instructions. The outcome of first addition is
Packit c4476c
	//	guaranteed to be zero, which leaves two computationally
Packit c4476c
	//	significant outcomes: it either carries or not. Then
Packit c4476c
	//	question is when does it carry? Is there alternative
Packit c4476c
	//	way to deduce it? If you follow operations, you can
Packit c4476c
	//	observe that condition for carry is quite simple:
Packit c4476c
	//	$lo0 being non-zero. So that carry can be calculated
Packit c4476c
	//	by adding -1 to $lo0. That's what next instruction does.
Packit c4476c
	subs	xzr,$lo0,#1		// (*)
Packit c4476c
	umulh	$nhi,$nj,$m1
Packit c4476c
	adc	$hi1,$hi1,xzr
Packit c4476c
	cbz	$j,.L1st_skip
Packit c4476c
Packit c4476c
.L1st:
Packit c4476c
	ldr	$aj,[$ap],#8
Packit c4476c
	adds	$lo0,$alo,$hi0
Packit c4476c
	sub	$j,$j,#8		// j--
Packit c4476c
	adc	$hi0,$ahi,xzr
Packit c4476c
Packit c4476c
	ldr	$nj,[$np],#8
Packit c4476c
	adds	$lo1,$nlo,$hi1
Packit c4476c
	mul	$alo,$aj,$m0		// ap[j]*bp[0]
Packit c4476c
	adc	$hi1,$nhi,xzr
Packit c4476c
	umulh	$ahi,$aj,$m0
Packit c4476c
Packit c4476c
	adds	$lo1,$lo1,$lo0
Packit c4476c
	mul	$nlo,$nj,$m1		// np[j]*m1
Packit c4476c
	adc	$hi1,$hi1,xzr
Packit c4476c
	umulh	$nhi,$nj,$m1
Packit c4476c
	str	$lo1,[$tp],#8		// tp[j-1]
Packit c4476c
	cbnz	$j,.L1st
Packit c4476c
Packit c4476c
.L1st_skip:
Packit c4476c
	adds	$lo0,$alo,$hi0
Packit c4476c
	sub	$ap,$ap,$num		// rewind $ap
Packit c4476c
	adc	$hi0,$ahi,xzr
Packit c4476c
Packit c4476c
	adds	$lo1,$nlo,$hi1
Packit c4476c
	sub	$np,$np,$num		// rewind $np
Packit c4476c
	adc	$hi1,$nhi,xzr
Packit c4476c
Packit c4476c
	adds	$lo1,$lo1,$lo0
Packit c4476c
	sub	$i,$num,#8		// i=num-1
Packit c4476c
	adcs	$hi1,$hi1,$hi0
Packit c4476c
Packit c4476c
	adc	$ovf,xzr,xzr		// upmost overflow bit
Packit c4476c
	stp	$lo1,$hi1,[$tp]
Packit c4476c
Packit c4476c
.Louter:
Packit c4476c
	ldr	$m0,[$bp],#8		// bp[i]
Packit c4476c
	ldp	$hi0,$aj,[$ap],#16
Packit c4476c
	ldr	$tj,[sp]		// tp[0]
Packit c4476c
	add	$tp,sp,#8
Packit c4476c
Packit c4476c
	mul	$lo0,$hi0,$m0		// ap[0]*bp[i]
Packit c4476c
	sub	$j,$num,#16		// j=num-2
Packit c4476c
	umulh	$hi0,$hi0,$m0
Packit c4476c
	ldp	$hi1,$nj,[$np],#16
Packit c4476c
	mul	$alo,$aj,$m0		// ap[1]*bp[i]
Packit c4476c
	adds	$lo0,$lo0,$tj
Packit c4476c
	umulh	$ahi,$aj,$m0
Packit c4476c
	adc	$hi0,$hi0,xzr
Packit c4476c
Packit c4476c
	mul	$m1,$lo0,$n0
Packit c4476c
	sub	$i,$i,#8		// i--
Packit c4476c
Packit c4476c
	// (*)	mul	$lo1,$hi1,$m1	// np[0]*m1
Packit c4476c
	umulh	$hi1,$hi1,$m1
Packit c4476c
	mul	$nlo,$nj,$m1		// np[1]*m1
Packit c4476c
	// (*)	adds	$lo1,$lo1,$lo0
Packit c4476c
	subs	xzr,$lo0,#1		// (*)
Packit c4476c
	umulh	$nhi,$nj,$m1
Packit c4476c
	cbz	$j,.Linner_skip
Packit c4476c
Packit c4476c
.Linner:
Packit c4476c
	ldr	$aj,[$ap],#8
Packit c4476c
	adc	$hi1,$hi1,xzr
Packit c4476c
	ldr	$tj,[$tp],#8		// tp[j]
Packit c4476c
	adds	$lo0,$alo,$hi0
Packit c4476c
	sub	$j,$j,#8		// j--
Packit c4476c
	adc	$hi0,$ahi,xzr
Packit c4476c
Packit c4476c
	adds	$lo1,$nlo,$hi1
Packit c4476c
	ldr	$nj,[$np],#8
Packit c4476c
	adc	$hi1,$nhi,xzr
Packit c4476c
Packit c4476c
	mul	$alo,$aj,$m0		// ap[j]*bp[i]
Packit c4476c
	adds	$lo0,$lo0,$tj
Packit c4476c
	umulh	$ahi,$aj,$m0
Packit c4476c
	adc	$hi0,$hi0,xzr
Packit c4476c
Packit c4476c
	mul	$nlo,$nj,$m1		// np[j]*m1
Packit c4476c
	adds	$lo1,$lo1,$lo0
Packit c4476c
	umulh	$nhi,$nj,$m1
Packit c4476c
	str	$lo1,[$tp,#-16]		// tp[j-1]
Packit c4476c
	cbnz	$j,.Linner
Packit c4476c
Packit c4476c
.Linner_skip:
Packit c4476c
	ldr	$tj,[$tp],#8		// tp[j]
Packit c4476c
	adc	$hi1,$hi1,xzr
Packit c4476c
	adds	$lo0,$alo,$hi0
Packit c4476c
	sub	$ap,$ap,$num		// rewind $ap
Packit c4476c
	adc	$hi0,$ahi,xzr
Packit c4476c
Packit c4476c
	adds	$lo1,$nlo,$hi1
Packit c4476c
	sub	$np,$np,$num		// rewind $np
Packit c4476c
	adcs	$hi1,$nhi,$ovf
Packit c4476c
	adc	$ovf,xzr,xzr
Packit c4476c
Packit c4476c
	adds	$lo0,$lo0,$tj
Packit c4476c
	adc	$hi0,$hi0,xzr
Packit c4476c
Packit c4476c
	adds	$lo1,$lo1,$lo0
Packit c4476c
	adcs	$hi1,$hi1,$hi0
Packit c4476c
	adc	$ovf,$ovf,xzr		// upmost overflow bit
Packit c4476c
	stp	$lo1,$hi1,[$tp,#-16]
Packit c4476c
Packit c4476c
	cbnz	$i,.Louter
Packit c4476c
Packit c4476c
	// Final step. We see if result is larger than modulus, and
Packit c4476c
	// if it is, subtract the modulus. But comparison implies
Packit c4476c
	// subtraction. So we subtract modulus, see if it borrowed,
Packit c4476c
	// and conditionally copy original value.
Packit c4476c
	ldr	$tj,[sp]		// tp[0]
Packit c4476c
	add	$tp,sp,#8
Packit c4476c
	ldr	$nj,[$np],#8		// np[0]
Packit c4476c
	subs	$j,$num,#8		// j=num-1 and clear borrow
Packit c4476c
	mov	$ap,$rp
Packit c4476c
.Lsub:
Packit c4476c
	sbcs	$aj,$tj,$nj		// tp[j]-np[j]
Packit c4476c
	ldr	$tj,[$tp],#8
Packit c4476c
	sub	$j,$j,#8		// j--
Packit c4476c
	ldr	$nj,[$np],#8
Packit c4476c
	str	$aj,[$ap],#8		// rp[j]=tp[j]-np[j]
Packit c4476c
	cbnz	$j,.Lsub
Packit c4476c
Packit c4476c
	sbcs	$aj,$tj,$nj
Packit c4476c
	sbcs	$ovf,$ovf,xzr		// did it borrow?
Packit c4476c
	str	$aj,[$ap],#8		// rp[num-1]
Packit c4476c
Packit c4476c
	ldr	$tj,[sp]		// tp[0]
Packit c4476c
	add	$tp,sp,#8
Packit c4476c
	ldr	$aj,[$rp],#8		// rp[0]
Packit c4476c
	sub	$num,$num,#8		// num--
Packit c4476c
	nop
Packit c4476c
.Lcond_copy:
Packit c4476c
	sub	$num,$num,#8		// num--
Packit c4476c
	csel	$nj,$tj,$aj,lo		// did it borrow?
Packit c4476c
	ldr	$tj,[$tp],#8
Packit c4476c
	ldr	$aj,[$rp],#8
Packit c4476c
	str	xzr,[$tp,#-16]		// wipe tp
Packit c4476c
	str	$nj,[$rp,#-16]
Packit c4476c
	cbnz	$num,.Lcond_copy
Packit c4476c
Packit c4476c
	csel	$nj,$tj,$aj,lo
Packit c4476c
	str	xzr,[$tp,#-8]		// wipe tp
Packit c4476c
	str	$nj,[$rp,#-8]
Packit c4476c
Packit c4476c
	ldp	x19,x20,[x29,#16]
Packit c4476c
	mov	sp,x29
Packit c4476c
	ldp	x21,x22,[x29,#32]
Packit c4476c
	mov	x0,#1
Packit c4476c
	ldp	x23,x24,[x29,#48]
Packit c4476c
	ldr	x29,[sp],#64
Packit c4476c
	ret
Packit c4476c
.size	bn_mul_mont,.-bn_mul_mont
Packit c4476c
___
Packit c4476c
{
Packit c4476c
########################################################################
Packit c4476c
# Following is ARMv8 adaptation of sqrx8x_mont from x86_64-mont5 module.
Packit c4476c
Packit c4476c
my ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("x$_",(6..13));
Packit c4476c
my ($t0,$t1,$t2,$t3)=map("x$_",(14..17));
Packit c4476c
my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("x$_",(19..26));
Packit c4476c
my ($cnt,$carry,$topmost)=("x27","x28","x30");
Packit c4476c
my ($tp,$ap_end,$na0)=($bp,$np,$carry);
Packit c4476c
Packit c4476c
$code.=<<___;
Packit c4476c
.type	__bn_sqr8x_mont,%function
Packit c4476c
.align	5
Packit c4476c
__bn_sqr8x_mont:
Packit c4476c
	cmp	$ap,$bp
Packit c4476c
	b.ne	__bn_mul4x_mont
Packit c4476c
.Lsqr8x_mont:
Packit c4476c
	.inst	0xd503233f		// paciasp
Packit c4476c
	stp	x29,x30,[sp,#-128]!
Packit c4476c
	add	x29,sp,#0
Packit c4476c
	stp	x19,x20,[sp,#16]
Packit c4476c
	stp	x21,x22,[sp,#32]
Packit c4476c
	stp	x23,x24,[sp,#48]
Packit c4476c
	stp	x25,x26,[sp,#64]
Packit c4476c
	stp	x27,x28,[sp,#80]
Packit c4476c
	stp	$rp,$np,[sp,#96]	// offload rp and np
Packit c4476c
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	ldp	$a4,$a5,[$ap,#8*4]
Packit c4476c
	ldp	$a6,$a7,[$ap,#8*6]
Packit c4476c
Packit c4476c
	sub	$tp,sp,$num,lsl#4
Packit c4476c
	lsl	$num,$num,#3
Packit c4476c
	ldr	$n0,[$n0]		// *n0
Packit c4476c
	mov	sp,$tp			// alloca
Packit c4476c
	sub	$cnt,$num,#8*8
Packit c4476c
	b	.Lsqr8x_zero_start
Packit c4476c
Packit c4476c
.Lsqr8x_zero:
Packit c4476c
	sub	$cnt,$cnt,#8*8
Packit c4476c
	stp	xzr,xzr,[$tp,#8*0]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*2]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*4]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*6]
Packit c4476c
.Lsqr8x_zero_start:
Packit c4476c
	stp	xzr,xzr,[$tp,#8*8]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*10]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*12]
Packit c4476c
	stp	xzr,xzr,[$tp,#8*14]
Packit c4476c
	add	$tp,$tp,#8*16
Packit c4476c
	cbnz	$cnt,.Lsqr8x_zero
Packit c4476c
Packit c4476c
	add	$ap_end,$ap,$num
Packit c4476c
	add	$ap,$ap,#8*8
Packit c4476c
	mov	$acc0,xzr
Packit c4476c
	mov	$acc1,xzr
Packit c4476c
	mov	$acc2,xzr
Packit c4476c
	mov	$acc3,xzr
Packit c4476c
	mov	$acc4,xzr
Packit c4476c
	mov	$acc5,xzr
Packit c4476c
	mov	$acc6,xzr
Packit c4476c
	mov	$acc7,xzr
Packit c4476c
	mov	$tp,sp
Packit c4476c
	str	$n0,[x29,#112]		// offload n0
Packit c4476c
Packit c4476c
	// Multiply everything but a[i]*a[i]
Packit c4476c
.align	4
Packit c4476c
.Lsqr8x_outer_loop:
Packit c4476c
        //                                                 a[1]a[0]	(i)
Packit c4476c
        //                                             a[2]a[0]
Packit c4476c
        //                                         a[3]a[0]
Packit c4476c
        //                                     a[4]a[0]
Packit c4476c
        //                                 a[5]a[0]
Packit c4476c
        //                             a[6]a[0]
Packit c4476c
        //                         a[7]a[0]
Packit c4476c
        //                                         a[2]a[1]		(ii)
Packit c4476c
        //                                     a[3]a[1]
Packit c4476c
        //                                 a[4]a[1]
Packit c4476c
        //                             a[5]a[1]
Packit c4476c
        //                         a[6]a[1]
Packit c4476c
        //                     a[7]a[1]
Packit c4476c
        //                                 a[3]a[2]			(iii)
Packit c4476c
        //                             a[4]a[2]
Packit c4476c
        //                         a[5]a[2]
Packit c4476c
        //                     a[6]a[2]
Packit c4476c
        //                 a[7]a[2]
Packit c4476c
        //                         a[4]a[3]				(iv)
Packit c4476c
        //                     a[5]a[3]
Packit c4476c
        //                 a[6]a[3]
Packit c4476c
        //             a[7]a[3]
Packit c4476c
        //                 a[5]a[4]					(v)
Packit c4476c
        //             a[6]a[4]
Packit c4476c
        //         a[7]a[4]
Packit c4476c
        //         a[6]a[5]						(vi)
Packit c4476c
        //     a[7]a[5]
Packit c4476c
        // a[7]a[6]							(vii)
Packit c4476c
Packit c4476c
	mul	$t0,$a1,$a0		// lo(a[1..7]*a[0])		(i)
Packit c4476c
	mul	$t1,$a2,$a0
Packit c4476c
	mul	$t2,$a3,$a0
Packit c4476c
	mul	$t3,$a4,$a0
Packit c4476c
	adds	$acc1,$acc1,$t0		// t[1]+lo(a[1]*a[0])
Packit c4476c
	mul	$t0,$a5,$a0
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	mul	$t1,$a6,$a0
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$a7,$a0
Packit c4476c
	adcs	$acc4,$acc4,$t3
Packit c4476c
	umulh	$t3,$a1,$a0		// hi(a[1..7]*a[0])
Packit c4476c
	adcs	$acc5,$acc5,$t0
Packit c4476c
	umulh	$t0,$a2,$a0
Packit c4476c
	adcs	$acc6,$acc6,$t1
Packit c4476c
	umulh	$t1,$a3,$a0
Packit c4476c
	adcs	$acc7,$acc7,$t2
Packit c4476c
	umulh	$t2,$a4,$a0
Packit c4476c
	stp	$acc0,$acc1,[$tp],#8*2	// t[0..1]
Packit c4476c
	adc	$acc0,xzr,xzr		// t[8]
Packit c4476c
	adds	$acc2,$acc2,$t3		// t[2]+lo(a[1]*a[0])
Packit c4476c
	umulh	$t3,$a5,$a0
Packit c4476c
	adcs	$acc3,$acc3,$t0
Packit c4476c
	umulh	$t0,$a6,$a0
Packit c4476c
	adcs	$acc4,$acc4,$t1
Packit c4476c
	umulh	$t1,$a7,$a0
Packit c4476c
	adcs	$acc5,$acc5,$t2
Packit c4476c
	 mul	$t2,$a2,$a1		// lo(a[2..7]*a[1])		(ii)
Packit c4476c
	adcs	$acc6,$acc6,$t3
Packit c4476c
	 mul	$t3,$a3,$a1
Packit c4476c
	adcs	$acc7,$acc7,$t0
Packit c4476c
	 mul	$t0,$a4,$a1
Packit c4476c
	adc	$acc0,$acc0,$t1
Packit c4476c
Packit c4476c
	mul	$t1,$a5,$a1
Packit c4476c
	adds	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$a6,$a1
Packit c4476c
	adcs	$acc4,$acc4,$t3
Packit c4476c
	mul	$t3,$a7,$a1
Packit c4476c
	adcs	$acc5,$acc5,$t0
Packit c4476c
	umulh	$t0,$a2,$a1		// hi(a[2..7]*a[1])
Packit c4476c
	adcs	$acc6,$acc6,$t1
Packit c4476c
	umulh	$t1,$a3,$a1
Packit c4476c
	adcs	$acc7,$acc7,$t2
Packit c4476c
	umulh	$t2,$a4,$a1
Packit c4476c
	adcs	$acc0,$acc0,$t3
Packit c4476c
	umulh	$t3,$a5,$a1
Packit c4476c
	stp	$acc2,$acc3,[$tp],#8*2	// t[2..3]
Packit c4476c
	adc	$acc1,xzr,xzr		// t[9]
Packit c4476c
	adds	$acc4,$acc4,$t0
Packit c4476c
	umulh	$t0,$a6,$a1
Packit c4476c
	adcs	$acc5,$acc5,$t1
Packit c4476c
	umulh	$t1,$a7,$a1
Packit c4476c
	adcs	$acc6,$acc6,$t2
Packit c4476c
	 mul	$t2,$a3,$a2		// lo(a[3..7]*a[2])		(iii)
Packit c4476c
	adcs	$acc7,$acc7,$t3
Packit c4476c
	 mul	$t3,$a4,$a2
Packit c4476c
	adcs	$acc0,$acc0,$t0
Packit c4476c
	 mul	$t0,$a5,$a2
Packit c4476c
	adc	$acc1,$acc1,$t1
Packit c4476c
Packit c4476c
	mul	$t1,$a6,$a2
Packit c4476c
	adds	$acc5,$acc5,$t2
Packit c4476c
	mul	$t2,$a7,$a2
Packit c4476c
	adcs	$acc6,$acc6,$t3
Packit c4476c
	umulh	$t3,$a3,$a2		// hi(a[3..7]*a[2])
Packit c4476c
	adcs	$acc7,$acc7,$t0
Packit c4476c
	umulh	$t0,$a4,$a2
Packit c4476c
	adcs	$acc0,$acc0,$t1
Packit c4476c
	umulh	$t1,$a5,$a2
Packit c4476c
	adcs	$acc1,$acc1,$t2
Packit c4476c
	umulh	$t2,$a6,$a2
Packit c4476c
	stp	$acc4,$acc5,[$tp],#8*2	// t[4..5]
Packit c4476c
	adc	$acc2,xzr,xzr		// t[10]
Packit c4476c
	adds	$acc6,$acc6,$t3
Packit c4476c
	umulh	$t3,$a7,$a2
Packit c4476c
	adcs	$acc7,$acc7,$t0
Packit c4476c
	 mul	$t0,$a4,$a3		// lo(a[4..7]*a[3])		(iv)
Packit c4476c
	adcs	$acc0,$acc0,$t1
Packit c4476c
	 mul	$t1,$a5,$a3
Packit c4476c
	adcs	$acc1,$acc1,$t2
Packit c4476c
	 mul	$t2,$a6,$a3
Packit c4476c
	adc	$acc2,$acc2,$t3
Packit c4476c
Packit c4476c
	mul	$t3,$a7,$a3
Packit c4476c
	adds	$acc7,$acc7,$t0
Packit c4476c
	umulh	$t0,$a4,$a3		// hi(a[4..7]*a[3])
Packit c4476c
	adcs	$acc0,$acc0,$t1
Packit c4476c
	umulh	$t1,$a5,$a3
Packit c4476c
	adcs	$acc1,$acc1,$t2
Packit c4476c
	umulh	$t2,$a6,$a3
Packit c4476c
	adcs	$acc2,$acc2,$t3
Packit c4476c
	umulh	$t3,$a7,$a3
Packit c4476c
	stp	$acc6,$acc7,[$tp],#8*2	// t[6..7]
Packit c4476c
	adc	$acc3,xzr,xzr		// t[11]
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	 mul	$t0,$a5,$a4		// lo(a[5..7]*a[4])		(v)
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	 mul	$t1,$a6,$a4
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	 mul	$t2,$a7,$a4
Packit c4476c
	adc	$acc3,$acc3,$t3
Packit c4476c
Packit c4476c
	umulh	$t3,$a5,$a4		// hi(a[5..7]*a[4])
Packit c4476c
	adds	$acc1,$acc1,$t0
Packit c4476c
	umulh	$t0,$a6,$a4
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	umulh	$t1,$a7,$a4
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	 mul	$t2,$a6,$a5		// lo(a[6..7]*a[5])		(vi)
Packit c4476c
	adc	$acc4,xzr,xzr		// t[12]
Packit c4476c
	adds	$acc2,$acc2,$t3
Packit c4476c
	 mul	$t3,$a7,$a5
Packit c4476c
	adcs	$acc3,$acc3,$t0
Packit c4476c
	 umulh	$t0,$a6,$a5		// hi(a[6..7]*a[5])
Packit c4476c
	adc	$acc4,$acc4,$t1
Packit c4476c
Packit c4476c
	umulh	$t1,$a7,$a5
Packit c4476c
	adds	$acc3,$acc3,$t2
Packit c4476c
	 mul	$t2,$a7,$a6		// lo(a[7]*a[6])		(vii)
Packit c4476c
	adcs	$acc4,$acc4,$t3
Packit c4476c
	 umulh	$t3,$a7,$a6		// hi(a[7]*a[6])
Packit c4476c
	adc	$acc5,xzr,xzr		// t[13]
Packit c4476c
	adds	$acc4,$acc4,$t0
Packit c4476c
	sub	$cnt,$ap_end,$ap	// done yet?
Packit c4476c
	adc	$acc5,$acc5,$t1
Packit c4476c
Packit c4476c
	adds	$acc5,$acc5,$t2
Packit c4476c
	sub	$t0,$ap_end,$num	// rewinded ap
Packit c4476c
	adc	$acc6,xzr,xzr		// t[14]
Packit c4476c
	add	$acc6,$acc6,$t3
Packit c4476c
Packit c4476c
	cbz	$cnt,.Lsqr8x_outer_break
Packit c4476c
Packit c4476c
	mov	$n0,$a0
Packit c4476c
	ldp	$a0,$a1,[$tp,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$tp,#8*2]
Packit c4476c
	ldp	$a4,$a5,[$tp,#8*4]
Packit c4476c
	ldp	$a6,$a7,[$tp,#8*6]
Packit c4476c
	adds	$acc0,$acc0,$a0
Packit c4476c
	adcs	$acc1,$acc1,$a1
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	adcs	$acc2,$acc2,$a2
Packit c4476c
	adcs	$acc3,$acc3,$a3
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	adcs	$acc4,$acc4,$a4
Packit c4476c
	adcs	$acc5,$acc5,$a5
Packit c4476c
	ldp	$a4,$a5,[$ap,#8*4]
Packit c4476c
	adcs	$acc6,$acc6,$a6
Packit c4476c
	mov	$rp,$ap
Packit c4476c
	adcs	$acc7,xzr,$a7
Packit c4476c
	ldp	$a6,$a7,[$ap,#8*6]
Packit c4476c
	add	$ap,$ap,#8*8
Packit c4476c
	//adc	$carry,xzr,xzr		// moved below
Packit c4476c
	mov	$cnt,#-8*8
Packit c4476c
Packit c4476c
	//                                                         a[8]a[0]
Packit c4476c
	//                                                     a[9]a[0]
Packit c4476c
	//                                                 a[a]a[0]
Packit c4476c
	//                                             a[b]a[0]
Packit c4476c
	//                                         a[c]a[0]
Packit c4476c
	//                                     a[d]a[0]
Packit c4476c
	//                                 a[e]a[0]
Packit c4476c
	//                             a[f]a[0]
Packit c4476c
	//                                                     a[8]a[1]
Packit c4476c
	//                         a[f]a[1]........................
Packit c4476c
	//                                                 a[8]a[2]
Packit c4476c
	//                     a[f]a[2]........................
Packit c4476c
	//                                             a[8]a[3]
Packit c4476c
	//                 a[f]a[3]........................
Packit c4476c
	//                                         a[8]a[4]
Packit c4476c
	//             a[f]a[4]........................
Packit c4476c
	//                                     a[8]a[5]
Packit c4476c
	//         a[f]a[5]........................
Packit c4476c
	//                                 a[8]a[6]
Packit c4476c
	//     a[f]a[6]........................
Packit c4476c
	//                             a[8]a[7]
Packit c4476c
	// a[f]a[7]........................
Packit c4476c
.Lsqr8x_mul:
Packit c4476c
	mul	$t0,$a0,$n0
Packit c4476c
	adc	$carry,xzr,xzr		// carry bit, modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$n0
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$n0
Packit c4476c
	mul	$t3,$a3,$n0
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	mul	$t0,$a4,$n0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	mul	$t1,$a5,$n0
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	mul	$t2,$a6,$n0
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	mul	$t3,$a7,$n0
Packit c4476c
	adcs	$acc4,$acc4,$t0
Packit c4476c
	umulh	$t0,$a0,$n0
Packit c4476c
	adcs	$acc5,$acc5,$t1
Packit c4476c
	umulh	$t1,$a1,$n0
Packit c4476c
	adcs	$acc6,$acc6,$t2
Packit c4476c
	umulh	$t2,$a2,$n0
Packit c4476c
	adcs	$acc7,$acc7,$t3
Packit c4476c
	umulh	$t3,$a3,$n0
Packit c4476c
	adc	$carry,$carry,xzr
Packit c4476c
	str	$acc0,[$tp],#8
Packit c4476c
	adds	$acc0,$acc1,$t0
Packit c4476c
	umulh	$t0,$a4,$n0
Packit c4476c
	adcs	$acc1,$acc2,$t1
Packit c4476c
	umulh	$t1,$a5,$n0
Packit c4476c
	adcs	$acc2,$acc3,$t2
Packit c4476c
	umulh	$t2,$a6,$n0
Packit c4476c
	adcs	$acc3,$acc4,$t3
Packit c4476c
	umulh	$t3,$a7,$n0
Packit c4476c
	ldr	$n0,[$rp,$cnt]
Packit c4476c
	adcs	$acc4,$acc5,$t0
Packit c4476c
	adcs	$acc5,$acc6,$t1
Packit c4476c
	adcs	$acc6,$acc7,$t2
Packit c4476c
	adcs	$acc7,$carry,$t3
Packit c4476c
	//adc	$carry,xzr,xzr		// moved above
Packit c4476c
	cbnz	$cnt,.Lsqr8x_mul
Packit c4476c
					// note that carry flag is guaranteed
Packit c4476c
					// to be zero at this point
Packit c4476c
	cmp	$ap,$ap_end		// done yet?
Packit c4476c
	b.eq	.Lsqr8x_break
Packit c4476c
Packit c4476c
	ldp	$a0,$a1,[$tp,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$tp,#8*2]
Packit c4476c
	ldp	$a4,$a5,[$tp,#8*4]
Packit c4476c
	ldp	$a6,$a7,[$tp,#8*6]
Packit c4476c
	adds	$acc0,$acc0,$a0
Packit c4476c
	ldr	$n0,[$rp,#-8*8]
Packit c4476c
	adcs	$acc1,$acc1,$a1
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	adcs	$acc2,$acc2,$a2
Packit c4476c
	adcs	$acc3,$acc3,$a3
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	adcs	$acc4,$acc4,$a4
Packit c4476c
	adcs	$acc5,$acc5,$a5
Packit c4476c
	ldp	$a4,$a5,[$ap,#8*4]
Packit c4476c
	adcs	$acc6,$acc6,$a6
Packit c4476c
	mov	$cnt,#-8*8
Packit c4476c
	adcs	$acc7,$acc7,$a7
Packit c4476c
	ldp	$a6,$a7,[$ap,#8*6]
Packit c4476c
	add	$ap,$ap,#8*8
Packit c4476c
	//adc	$carry,xzr,xzr		// moved above
Packit c4476c
	b	.Lsqr8x_mul
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lsqr8x_break:
Packit c4476c
	ldp	$a0,$a1,[$rp,#8*0]
Packit c4476c
	add	$ap,$rp,#8*8
Packit c4476c
	ldp	$a2,$a3,[$rp,#8*2]
Packit c4476c
	sub	$t0,$ap_end,$ap		// is it last iteration?
Packit c4476c
	ldp	$a4,$a5,[$rp,#8*4]
Packit c4476c
	sub	$t1,$tp,$t0
Packit c4476c
	ldp	$a6,$a7,[$rp,#8*6]
Packit c4476c
	cbz	$t0,.Lsqr8x_outer_loop
Packit c4476c
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	ldp	$acc0,$acc1,[$t1,#8*0]
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	ldp	$acc2,$acc3,[$t1,#8*2]
Packit c4476c
	stp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	ldp	$acc4,$acc5,[$t1,#8*4]
Packit c4476c
	stp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	mov	$tp,$t1
Packit c4476c
	ldp	$acc6,$acc7,[$t1,#8*6]
Packit c4476c
	b	.Lsqr8x_outer_loop
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lsqr8x_outer_break:
Packit c4476c
	// Now multiply above result by 2 and add a[n-1]*a[n-1]|...|a[0]*a[0]
Packit c4476c
	ldp	$a1,$a3,[$t0,#8*0]	// recall that $t0 is &a[0]
Packit c4476c
	ldp	$t1,$t2,[sp,#8*1]
Packit c4476c
	ldp	$a5,$a7,[$t0,#8*2]
Packit c4476c
	add	$ap,$t0,#8*4
Packit c4476c
	ldp	$t3,$t0,[sp,#8*3]
Packit c4476c
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	mul	$acc0,$a1,$a1
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	umulh	$a1,$a1,$a1
Packit c4476c
	stp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	mul	$a2,$a3,$a3
Packit c4476c
	stp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	mov	$tp,sp
Packit c4476c
	umulh	$a3,$a3,$a3
Packit c4476c
	adds	$acc1,$a1,$t1,lsl#1
Packit c4476c
	extr	$t1,$t2,$t1,#63
Packit c4476c
	sub	$cnt,$num,#8*4
Packit c4476c
Packit c4476c
.Lsqr4x_shift_n_add:
Packit c4476c
	adcs	$acc2,$a2,$t1
Packit c4476c
	extr	$t2,$t3,$t2,#63
Packit c4476c
	sub	$cnt,$cnt,#8*4
Packit c4476c
	adcs	$acc3,$a3,$t2
Packit c4476c
	ldp	$t1,$t2,[$tp,#8*5]
Packit c4476c
	mul	$a4,$a5,$a5
Packit c4476c
	ldp	$a1,$a3,[$ap],#8*2
Packit c4476c
	umulh	$a5,$a5,$a5
Packit c4476c
	mul	$a6,$a7,$a7
Packit c4476c
	umulh	$a7,$a7,$a7
Packit c4476c
	extr	$t3,$t0,$t3,#63
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	adcs	$acc4,$a4,$t3
Packit c4476c
	extr	$t0,$t1,$t0,#63
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	adcs	$acc5,$a5,$t0
Packit c4476c
	ldp	$t3,$t0,[$tp,#8*7]
Packit c4476c
	extr	$t1,$t2,$t1,#63
Packit c4476c
	adcs	$acc6,$a6,$t1
Packit c4476c
	extr	$t2,$t3,$t2,#63
Packit c4476c
	adcs	$acc7,$a7,$t2
Packit c4476c
	ldp	$t1,$t2,[$tp,#8*9]
Packit c4476c
	mul	$a0,$a1,$a1
Packit c4476c
	ldp	$a5,$a7,[$ap],#8*2
Packit c4476c
	umulh	$a1,$a1,$a1
Packit c4476c
	mul	$a2,$a3,$a3
Packit c4476c
	umulh	$a3,$a3,$a3
Packit c4476c
	stp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	extr	$t3,$t0,$t3,#63
Packit c4476c
	stp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	add	$tp,$tp,#8*8
Packit c4476c
	adcs	$acc0,$a0,$t3
Packit c4476c
	extr	$t0,$t1,$t0,#63
Packit c4476c
	adcs	$acc1,$a1,$t0
Packit c4476c
	ldp	$t3,$t0,[$tp,#8*3]
Packit c4476c
	extr	$t1,$t2,$t1,#63
Packit c4476c
	cbnz	$cnt,.Lsqr4x_shift_n_add
Packit c4476c
___
Packit c4476c
my ($np,$np_end)=($ap,$ap_end);
Packit c4476c
$code.=<<___;
Packit c4476c
	 ldp	$np,$n0,[x29,#104]	// pull np and n0
Packit c4476c
Packit c4476c
	adcs	$acc2,$a2,$t1
Packit c4476c
	extr	$t2,$t3,$t2,#63
Packit c4476c
	adcs	$acc3,$a3,$t2
Packit c4476c
	ldp	$t1,$t2,[$tp,#8*5]
Packit c4476c
	mul	$a4,$a5,$a5
Packit c4476c
	umulh	$a5,$a5,$a5
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	mul	$a6,$a7,$a7
Packit c4476c
	umulh	$a7,$a7,$a7
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	extr	$t3,$t0,$t3,#63
Packit c4476c
	adcs	$acc4,$a4,$t3
Packit c4476c
	extr	$t0,$t1,$t0,#63
Packit c4476c
	 ldp	$acc0,$acc1,[sp,#8*0]
Packit c4476c
	adcs	$acc5,$a5,$t0
Packit c4476c
	extr	$t1,$t2,$t1,#63
Packit c4476c
	 ldp	$a0,$a1,[$np,#8*0]
Packit c4476c
	adcs	$acc6,$a6,$t1
Packit c4476c
	extr	$t2,xzr,$t2,#63
Packit c4476c
	 ldp	$a2,$a3,[$np,#8*2]
Packit c4476c
	adc	$acc7,$a7,$t2
Packit c4476c
	 ldp	$a4,$a5,[$np,#8*4]
Packit c4476c
Packit c4476c
	// Reduce by 512 bits per iteration
Packit c4476c
	mul	$na0,$n0,$acc0		// t[0]*n0
Packit c4476c
	ldp	$a6,$a7,[$np,#8*6]
Packit c4476c
	add	$np_end,$np,$num
Packit c4476c
	ldp	$acc2,$acc3,[sp,#8*2]
Packit c4476c
	stp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	ldp	$acc4,$acc5,[sp,#8*4]
Packit c4476c
	stp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	ldp	$acc6,$acc7,[sp,#8*6]
Packit c4476c
	add	$np,$np,#8*8
Packit c4476c
	mov	$topmost,xzr		// initial top-most carry
Packit c4476c
	mov	$tp,sp
Packit c4476c
	mov	$cnt,#8
Packit c4476c
Packit c4476c
.Lsqr8x_reduction:
Packit c4476c
	// (*)	mul	$t0,$a0,$na0	// lo(n[0-7])*lo(t[0]*n0)
Packit c4476c
	mul	$t1,$a1,$na0
Packit c4476c
	sub	$cnt,$cnt,#1
Packit c4476c
	mul	$t2,$a2,$na0
Packit c4476c
	str	$na0,[$tp],#8		// put aside t[0]*n0 for tail processing
Packit c4476c
	mul	$t3,$a3,$na0
Packit c4476c
	// (*)	adds	xzr,$acc0,$t0
Packit c4476c
	subs	xzr,$acc0,#1		// (*)
Packit c4476c
	mul	$t0,$a4,$na0
Packit c4476c
	adcs	$acc0,$acc1,$t1
Packit c4476c
	mul	$t1,$a5,$na0
Packit c4476c
	adcs	$acc1,$acc2,$t2
Packit c4476c
	mul	$t2,$a6,$na0
Packit c4476c
	adcs	$acc2,$acc3,$t3
Packit c4476c
	mul	$t3,$a7,$na0
Packit c4476c
	adcs	$acc3,$acc4,$t0
Packit c4476c
	umulh	$t0,$a0,$na0		// hi(n[0-7])*lo(t[0]*n0)
Packit c4476c
	adcs	$acc4,$acc5,$t1
Packit c4476c
	umulh	$t1,$a1,$na0
Packit c4476c
	adcs	$acc5,$acc6,$t2
Packit c4476c
	umulh	$t2,$a2,$na0
Packit c4476c
	adcs	$acc6,$acc7,$t3
Packit c4476c
	umulh	$t3,$a3,$na0
Packit c4476c
	adc	$acc7,xzr,xzr
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$a4,$na0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	umulh	$t1,$a5,$na0
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t2,$a6,$na0
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t3,$a7,$na0
Packit c4476c
	mul	$na0,$n0,$acc0		// next t[0]*n0
Packit c4476c
	adcs	$acc4,$acc4,$t0
Packit c4476c
	adcs	$acc5,$acc5,$t1
Packit c4476c
	adcs	$acc6,$acc6,$t2
Packit c4476c
	adc	$acc7,$acc7,$t3
Packit c4476c
	cbnz	$cnt,.Lsqr8x_reduction
Packit c4476c
Packit c4476c
	ldp	$t0,$t1,[$tp,#8*0]
Packit c4476c
	ldp	$t2,$t3,[$tp,#8*2]
Packit c4476c
	mov	$rp,$tp
Packit c4476c
	sub	$cnt,$np_end,$np	// done yet?
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	ldp	$t0,$t1,[$tp,#8*4]
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	ldp	$t2,$t3,[$tp,#8*6]
Packit c4476c
	adcs	$acc4,$acc4,$t0
Packit c4476c
	adcs	$acc5,$acc5,$t1
Packit c4476c
	adcs	$acc6,$acc6,$t2
Packit c4476c
	adcs	$acc7,$acc7,$t3
Packit c4476c
	//adc	$carry,xzr,xzr		// moved below
Packit c4476c
	cbz	$cnt,.Lsqr8x8_post_condition
Packit c4476c
Packit c4476c
	ldr	$n0,[$tp,#-8*8]
Packit c4476c
	ldp	$a0,$a1,[$np,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$np,#8*2]
Packit c4476c
	ldp	$a4,$a5,[$np,#8*4]
Packit c4476c
	mov	$cnt,#-8*8
Packit c4476c
	ldp	$a6,$a7,[$np,#8*6]
Packit c4476c
	add	$np,$np,#8*8
Packit c4476c
Packit c4476c
.Lsqr8x_tail:
Packit c4476c
	mul	$t0,$a0,$n0
Packit c4476c
	adc	$carry,xzr,xzr		// carry bit, modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$n0
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$n0
Packit c4476c
	mul	$t3,$a3,$n0
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	mul	$t0,$a4,$n0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	mul	$t1,$a5,$n0
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	mul	$t2,$a6,$n0
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	mul	$t3,$a7,$n0
Packit c4476c
	adcs	$acc4,$acc4,$t0
Packit c4476c
	umulh	$t0,$a0,$n0
Packit c4476c
	adcs	$acc5,$acc5,$t1
Packit c4476c
	umulh	$t1,$a1,$n0
Packit c4476c
	adcs	$acc6,$acc6,$t2
Packit c4476c
	umulh	$t2,$a2,$n0
Packit c4476c
	adcs	$acc7,$acc7,$t3
Packit c4476c
	umulh	$t3,$a3,$n0
Packit c4476c
	adc	$carry,$carry,xzr
Packit c4476c
	str	$acc0,[$tp],#8
Packit c4476c
	adds	$acc0,$acc1,$t0
Packit c4476c
	umulh	$t0,$a4,$n0
Packit c4476c
	adcs	$acc1,$acc2,$t1
Packit c4476c
	umulh	$t1,$a5,$n0
Packit c4476c
	adcs	$acc2,$acc3,$t2
Packit c4476c
	umulh	$t2,$a6,$n0
Packit c4476c
	adcs	$acc3,$acc4,$t3
Packit c4476c
	umulh	$t3,$a7,$n0
Packit c4476c
	ldr	$n0,[$rp,$cnt]
Packit c4476c
	adcs	$acc4,$acc5,$t0
Packit c4476c
	adcs	$acc5,$acc6,$t1
Packit c4476c
	adcs	$acc6,$acc7,$t2
Packit c4476c
	adcs	$acc7,$carry,$t3
Packit c4476c
	//adc	$carry,xzr,xzr		// moved above
Packit c4476c
	cbnz	$cnt,.Lsqr8x_tail
Packit c4476c
					// note that carry flag is guaranteed
Packit c4476c
					// to be zero at this point
Packit c4476c
	ldp	$a0,$a1,[$tp,#8*0]
Packit c4476c
	sub	$cnt,$np_end,$np	// done yet?
Packit c4476c
	sub	$t2,$np_end,$num	// rewinded np
Packit c4476c
	ldp	$a2,$a3,[$tp,#8*2]
Packit c4476c
	ldp	$a4,$a5,[$tp,#8*4]
Packit c4476c
	ldp	$a6,$a7,[$tp,#8*6]
Packit c4476c
	cbz	$cnt,.Lsqr8x_tail_break
Packit c4476c
Packit c4476c
	ldr	$n0,[$rp,#-8*8]
Packit c4476c
	adds	$acc0,$acc0,$a0
Packit c4476c
	adcs	$acc1,$acc1,$a1
Packit c4476c
	ldp	$a0,$a1,[$np,#8*0]
Packit c4476c
	adcs	$acc2,$acc2,$a2
Packit c4476c
	adcs	$acc3,$acc3,$a3
Packit c4476c
	ldp	$a2,$a3,[$np,#8*2]
Packit c4476c
	adcs	$acc4,$acc4,$a4
Packit c4476c
	adcs	$acc5,$acc5,$a5
Packit c4476c
	ldp	$a4,$a5,[$np,#8*4]
Packit c4476c
	adcs	$acc6,$acc6,$a6
Packit c4476c
	mov	$cnt,#-8*8
Packit c4476c
	adcs	$acc7,$acc7,$a7
Packit c4476c
	ldp	$a6,$a7,[$np,#8*6]
Packit c4476c
	add	$np,$np,#8*8
Packit c4476c
	//adc	$carry,xzr,xzr		// moved above
Packit c4476c
	b	.Lsqr8x_tail
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lsqr8x_tail_break:
Packit c4476c
	ldr	$n0,[x29,#112]		// pull n0
Packit c4476c
	add	$cnt,$tp,#8*8		// end of current t[num] window
Packit c4476c
Packit c4476c
	subs	xzr,$topmost,#1		// "move" top-most carry to carry bit
Packit c4476c
	adcs	$t0,$acc0,$a0
Packit c4476c
	adcs	$t1,$acc1,$a1
Packit c4476c
	ldp	$acc0,$acc1,[$rp,#8*0]
Packit c4476c
	adcs	$acc2,$acc2,$a2
Packit c4476c
	ldp	$a0,$a1,[$t2,#8*0]	// recall that $t2 is &n[0]
Packit c4476c
	adcs	$acc3,$acc3,$a3
Packit c4476c
	ldp	$a2,$a3,[$t2,#8*2]
Packit c4476c
	adcs	$acc4,$acc4,$a4
Packit c4476c
	adcs	$acc5,$acc5,$a5
Packit c4476c
	ldp	$a4,$a5,[$t2,#8*4]
Packit c4476c
	adcs	$acc6,$acc6,$a6
Packit c4476c
	adcs	$acc7,$acc7,$a7
Packit c4476c
	ldp	$a6,$a7,[$t2,#8*6]
Packit c4476c
	add	$np,$t2,#8*8
Packit c4476c
	adc	$topmost,xzr,xzr	// top-most carry
Packit c4476c
	mul	$na0,$n0,$acc0
Packit c4476c
	stp	$t0,$t1,[$tp,#8*0]
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	ldp	$acc2,$acc3,[$rp,#8*2]
Packit c4476c
	stp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	ldp	$acc4,$acc5,[$rp,#8*4]
Packit c4476c
	cmp	$cnt,x29		// did we hit the bottom?
Packit c4476c
	stp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	mov	$tp,$rp			// slide the window
Packit c4476c
	ldp	$acc6,$acc7,[$rp,#8*6]
Packit c4476c
	mov	$cnt,#8
Packit c4476c
	b.ne	.Lsqr8x_reduction
Packit c4476c
Packit c4476c
	// Final step. We see if result is larger than modulus, and
Packit c4476c
	// if it is, subtract the modulus. But comparison implies
Packit c4476c
	// subtraction. So we subtract modulus, see if it borrowed,
Packit c4476c
	// and conditionally copy original value.
Packit c4476c
	ldr	$rp,[x29,#96]		// pull rp
Packit c4476c
	add	$tp,$tp,#8*8
Packit c4476c
	subs	$t0,$acc0,$a0
Packit c4476c
	sbcs	$t1,$acc1,$a1
Packit c4476c
	sub	$cnt,$num,#8*8
Packit c4476c
	mov	$ap_end,$rp		// $rp copy
Packit c4476c
Packit c4476c
.Lsqr8x_sub:
Packit c4476c
	sbcs	$t2,$acc2,$a2
Packit c4476c
	ldp	$a0,$a1,[$np,#8*0]
Packit c4476c
	sbcs	$t3,$acc3,$a3
Packit c4476c
	stp	$t0,$t1,[$rp,#8*0]
Packit c4476c
	sbcs	$t0,$acc4,$a4
Packit c4476c
	ldp	$a2,$a3,[$np,#8*2]
Packit c4476c
	sbcs	$t1,$acc5,$a5
Packit c4476c
	stp	$t2,$t3,[$rp,#8*2]
Packit c4476c
	sbcs	$t2,$acc6,$a6
Packit c4476c
	ldp	$a4,$a5,[$np,#8*4]
Packit c4476c
	sbcs	$t3,$acc7,$a7
Packit c4476c
	ldp	$a6,$a7,[$np,#8*6]
Packit c4476c
	add	$np,$np,#8*8
Packit c4476c
	ldp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	sub	$cnt,$cnt,#8*8
Packit c4476c
	ldp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	ldp	$acc4,$acc5,[$tp,#8*4]
Packit c4476c
	ldp	$acc6,$acc7,[$tp,#8*6]
Packit c4476c
	add	$tp,$tp,#8*8
Packit c4476c
	stp	$t0,$t1,[$rp,#8*4]
Packit c4476c
	sbcs	$t0,$acc0,$a0
Packit c4476c
	stp	$t2,$t3,[$rp,#8*6]
Packit c4476c
	add	$rp,$rp,#8*8
Packit c4476c
	sbcs	$t1,$acc1,$a1
Packit c4476c
	cbnz	$cnt,.Lsqr8x_sub
Packit c4476c
Packit c4476c
	sbcs	$t2,$acc2,$a2
Packit c4476c
	 mov	$tp,sp
Packit c4476c
	 add	$ap,sp,$num
Packit c4476c
	 ldp	$a0,$a1,[$ap_end,#8*0]
Packit c4476c
	sbcs	$t3,$acc3,$a3
Packit c4476c
	stp	$t0,$t1,[$rp,#8*0]
Packit c4476c
	sbcs	$t0,$acc4,$a4
Packit c4476c
	 ldp	$a2,$a3,[$ap_end,#8*2]
Packit c4476c
	sbcs	$t1,$acc5,$a5
Packit c4476c
	stp	$t2,$t3,[$rp,#8*2]
Packit c4476c
	sbcs	$t2,$acc6,$a6
Packit c4476c
	 ldp	$acc0,$acc1,[$ap,#8*0]
Packit c4476c
	sbcs	$t3,$acc7,$a7
Packit c4476c
	 ldp	$acc2,$acc3,[$ap,#8*2]
Packit c4476c
	sbcs	xzr,$topmost,xzr	// did it borrow?
Packit c4476c
	ldr	x30,[x29,#8]		// pull return address
Packit c4476c
	stp	$t0,$t1,[$rp,#8*4]
Packit c4476c
	stp	$t2,$t3,[$rp,#8*6]
Packit c4476c
Packit c4476c
	sub	$cnt,$num,#8*4
Packit c4476c
.Lsqr4x_cond_copy:
Packit c4476c
	sub	$cnt,$cnt,#8*4
Packit c4476c
	csel	$t0,$acc0,$a0,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*0]
Packit c4476c
	csel	$t1,$acc1,$a1,lo
Packit c4476c
	ldp	$a0,$a1,[$ap_end,#8*4]
Packit c4476c
	ldp	$acc0,$acc1,[$ap,#8*4]
Packit c4476c
	csel	$t2,$acc2,$a2,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*2]
Packit c4476c
	 add	$tp,$tp,#8*4
Packit c4476c
	csel	$t3,$acc3,$a3,lo
Packit c4476c
	ldp	$a2,$a3,[$ap_end,#8*6]
Packit c4476c
	ldp	$acc2,$acc3,[$ap,#8*6]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	stp	$t0,$t1,[$ap_end,#8*0]
Packit c4476c
	stp	$t2,$t3,[$ap_end,#8*2]
Packit c4476c
	add	$ap_end,$ap_end,#8*4
Packit c4476c
	 stp	xzr,xzr,[$ap,#8*0]
Packit c4476c
	 stp	xzr,xzr,[$ap,#8*2]
Packit c4476c
	cbnz	$cnt,.Lsqr4x_cond_copy
Packit c4476c
Packit c4476c
	csel	$t0,$acc0,$a0,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*0]
Packit c4476c
	csel	$t1,$acc1,$a1,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*2]
Packit c4476c
	csel	$t2,$acc2,$a2,lo
Packit c4476c
	csel	$t3,$acc3,$a3,lo
Packit c4476c
	stp	$t0,$t1,[$ap_end,#8*0]
Packit c4476c
	stp	$t2,$t3,[$ap_end,#8*2]
Packit c4476c
Packit c4476c
	b	.Lsqr8x_done
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lsqr8x8_post_condition:
Packit c4476c
	adc	$carry,xzr,xzr
Packit c4476c
	ldr	x30,[x29,#8]		// pull return address
Packit c4476c
	// $acc0-7,$carry hold result, $a0-7 hold modulus
Packit c4476c
	subs	$a0,$acc0,$a0
Packit c4476c
	ldr	$ap,[x29,#96]		// pull rp
Packit c4476c
	sbcs	$a1,$acc1,$a1
Packit c4476c
	 stp	xzr,xzr,[sp,#8*0]
Packit c4476c
	sbcs	$a2,$acc2,$a2
Packit c4476c
	 stp	xzr,xzr,[sp,#8*2]
Packit c4476c
	sbcs	$a3,$acc3,$a3
Packit c4476c
	 stp	xzr,xzr,[sp,#8*4]
Packit c4476c
	sbcs	$a4,$acc4,$a4
Packit c4476c
	 stp	xzr,xzr,[sp,#8*6]
Packit c4476c
	sbcs	$a5,$acc5,$a5
Packit c4476c
	 stp	xzr,xzr,[sp,#8*8]
Packit c4476c
	sbcs	$a6,$acc6,$a6
Packit c4476c
	 stp	xzr,xzr,[sp,#8*10]
Packit c4476c
	sbcs	$a7,$acc7,$a7
Packit c4476c
	 stp	xzr,xzr,[sp,#8*12]
Packit c4476c
	sbcs	$carry,$carry,xzr	// did it borrow?
Packit c4476c
	 stp	xzr,xzr,[sp,#8*14]
Packit c4476c
Packit c4476c
	// $a0-7 hold result-modulus
Packit c4476c
	csel	$a0,$acc0,$a0,lo
Packit c4476c
	csel	$a1,$acc1,$a1,lo
Packit c4476c
	csel	$a2,$acc2,$a2,lo
Packit c4476c
	csel	$a3,$acc3,$a3,lo
Packit c4476c
	stp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	csel	$a4,$acc4,$a4,lo
Packit c4476c
	csel	$a5,$acc5,$a5,lo
Packit c4476c
	stp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	csel	$a6,$acc6,$a6,lo
Packit c4476c
	csel	$a7,$acc7,$a7,lo
Packit c4476c
	stp	$a4,$a5,[$ap,#8*4]
Packit c4476c
	stp	$a6,$a7,[$ap,#8*6]
Packit c4476c
Packit c4476c
.Lsqr8x_done:
Packit c4476c
	ldp	x19,x20,[x29,#16]
Packit c4476c
	mov	sp,x29
Packit c4476c
	ldp	x21,x22,[x29,#32]
Packit c4476c
	mov	x0,#1
Packit c4476c
	ldp	x23,x24,[x29,#48]
Packit c4476c
	ldp	x25,x26,[x29,#64]
Packit c4476c
	ldp	x27,x28,[x29,#80]
Packit c4476c
	ldr	x29,[sp],#128
Packit c4476c
	.inst	0xd50323bf		// autiasp
Packit c4476c
	ret
Packit c4476c
.size	__bn_sqr8x_mont,.-__bn_sqr8x_mont
Packit c4476c
___
Packit c4476c
}
Packit c4476c
Packit c4476c
{
Packit c4476c
########################################################################
Packit c4476c
# Even though this might look as ARMv8 adaptation of mulx4x_mont from
Packit c4476c
# x86_64-mont5 module, it's different in sense that it performs
Packit c4476c
# reduction 256 bits at a time.
Packit c4476c
Packit c4476c
my ($a0,$a1,$a2,$a3,
Packit c4476c
    $t0,$t1,$t2,$t3,
Packit c4476c
    $m0,$m1,$m2,$m3,
Packit c4476c
    $acc0,$acc1,$acc2,$acc3,$acc4,
Packit c4476c
    $bi,$mi,$tp,$ap_end,$cnt) = map("x$_",(6..17,19..28));
Packit c4476c
my  $bp_end=$rp;
Packit c4476c
my  ($carry,$topmost) = ($rp,"x30");
Packit c4476c
Packit c4476c
$code.=<<___;
Packit c4476c
.type	__bn_mul4x_mont,%function
Packit c4476c
.align	5
Packit c4476c
__bn_mul4x_mont:
Packit c4476c
	.inst	0xd503233f		// paciasp
Packit c4476c
	stp	x29,x30,[sp,#-128]!
Packit c4476c
	add	x29,sp,#0
Packit c4476c
	stp	x19,x20,[sp,#16]
Packit c4476c
	stp	x21,x22,[sp,#32]
Packit c4476c
	stp	x23,x24,[sp,#48]
Packit c4476c
	stp	x25,x26,[sp,#64]
Packit c4476c
	stp	x27,x28,[sp,#80]
Packit c4476c
Packit c4476c
	sub	$tp,sp,$num,lsl#3
Packit c4476c
	lsl	$num,$num,#3
Packit c4476c
	ldr	$n0,[$n0]		// *n0
Packit c4476c
	sub	sp,$tp,#8*4		// alloca
Packit c4476c
Packit c4476c
	add	$t0,$bp,$num
Packit c4476c
	add	$ap_end,$ap,$num
Packit c4476c
	stp	$rp,$t0,[x29,#96]	// offload rp and &b[num]
Packit c4476c
Packit c4476c
	ldr	$bi,[$bp,#8*0]		// b[0]
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]	// a[0..3]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	mov	$acc0,xzr
Packit c4476c
	mov	$acc1,xzr
Packit c4476c
	mov	$acc2,xzr
Packit c4476c
	mov	$acc3,xzr
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]	// n[0..3]
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	adds	$np,$np,#8*4		// clear carry bit
Packit c4476c
	mov	$carry,xzr
Packit c4476c
	mov	$cnt,#0
Packit c4476c
	mov	$tp,sp
Packit c4476c
Packit c4476c
.Loop_mul4x_1st_reduction:
Packit c4476c
	mul	$t0,$a0,$bi		// lo(a[0..3]*b[0])
Packit c4476c
	adc	$carry,$carry,xzr	// modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$bi
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$bi
Packit c4476c
	and	$cnt,$cnt,#31
Packit c4476c
	mul	$t3,$a3,$bi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$a0,$bi		// hi(a[0..3]*b[0])
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	mul	$mi,$acc0,$n0		// t[0]*n0
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t1,$a1,$bi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t2,$a2,$bi
Packit c4476c
	adc	$acc4,xzr,xzr
Packit c4476c
	umulh	$t3,$a3,$bi
Packit c4476c
	ldr	$bi,[$bp,$cnt]		// next b[i] (or b[0])
Packit c4476c
	adds	$acc1,$acc1,$t0
Packit c4476c
	// (*)	mul	$t0,$m0,$mi	// lo(n[0..3]*t[0]*n0)
Packit c4476c
	str	$mi,[$tp],#8		// put aside t[0]*n0 for tail processing
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	mul	$t1,$m1,$mi
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$m2,$mi
Packit c4476c
	adc	$acc4,$acc4,$t3		// can't overflow
Packit c4476c
	mul	$t3,$m3,$mi
Packit c4476c
	// (*)	adds	xzr,$acc0,$t0
Packit c4476c
	subs	xzr,$acc0,#1		// (*)
Packit c4476c
	umulh	$t0,$m0,$mi		// hi(n[0..3]*t[0]*n0)
Packit c4476c
	adcs	$acc0,$acc1,$t1
Packit c4476c
	umulh	$t1,$m1,$mi
Packit c4476c
	adcs	$acc1,$acc2,$t2
Packit c4476c
	umulh	$t2,$m2,$mi
Packit c4476c
	adcs	$acc2,$acc3,$t3
Packit c4476c
	umulh	$t3,$m3,$mi
Packit c4476c
	adcs	$acc3,$acc4,$carry
Packit c4476c
	adc	$carry,xzr,xzr
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	sub	$t0,$ap_end,$ap
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
	cbnz	$cnt,.Loop_mul4x_1st_reduction
Packit c4476c
Packit c4476c
	cbz	$t0,.Lmul4x4_post_condition
Packit c4476c
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]	// a[4..7]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	ldr	$mi,[sp]		// a[0]*n0
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]	// n[4..7]
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	add	$np,$np,#8*4
Packit c4476c
Packit c4476c
.Loop_mul4x_1st_tail:
Packit c4476c
	mul	$t0,$a0,$bi		// lo(a[4..7]*b[i])
Packit c4476c
	adc	$carry,$carry,xzr	// modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$bi
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$bi
Packit c4476c
	and	$cnt,$cnt,#31
Packit c4476c
	mul	$t3,$a3,$bi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$a0,$bi		// hi(a[4..7]*b[i])
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	umulh	$t1,$a1,$bi
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t2,$a2,$bi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t3,$a3,$bi
Packit c4476c
	adc	$acc4,xzr,xzr
Packit c4476c
	ldr	$bi,[$bp,$cnt]		// next b[i] (or b[0])
Packit c4476c
	adds	$acc1,$acc1,$t0
Packit c4476c
	mul	$t0,$m0,$mi		// lo(n[4..7]*a[0]*n0)
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	mul	$t1,$m1,$mi
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$m2,$mi
Packit c4476c
	adc	$acc4,$acc4,$t3		// can't overflow
Packit c4476c
	mul	$t3,$m3,$mi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$m0,$mi		// hi(n[4..7]*a[0]*n0)
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	umulh	$t1,$m1,$mi
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t2,$m2,$mi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	adcs	$acc4,$acc4,$carry
Packit c4476c
	umulh	$t3,$m3,$mi
Packit c4476c
	adc	$carry,xzr,xzr
Packit c4476c
	ldr	$mi,[sp,$cnt]		// next t[0]*n0
Packit c4476c
	str	$acc0,[$tp],#8		// result!!!
Packit c4476c
	adds	$acc0,$acc1,$t0
Packit c4476c
	sub	$t0,$ap_end,$ap		// done yet?
Packit c4476c
	adcs	$acc1,$acc2,$t1
Packit c4476c
	adcs	$acc2,$acc3,$t2
Packit c4476c
	adcs	$acc3,$acc4,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
	cbnz	$cnt,.Loop_mul4x_1st_tail
Packit c4476c
Packit c4476c
	sub	$t1,$ap_end,$num	// rewinded $ap
Packit c4476c
	cbz	$t0,.Lmul4x_proceed
Packit c4476c
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	add	$np,$np,#8*4
Packit c4476c
	b	.Loop_mul4x_1st_tail
Packit c4476c
Packit c4476c
.align	5
Packit c4476c
.Lmul4x_proceed:
Packit c4476c
	ldr	$bi,[$bp,#8*4]!		// *++b
Packit c4476c
	adc	$topmost,$carry,xzr
Packit c4476c
	ldp	$a0,$a1,[$t1,#8*0]	// a[0..3]
Packit c4476c
	sub	$np,$np,$num		// rewind np
Packit c4476c
	ldp	$a2,$a3,[$t1,#8*2]
Packit c4476c
	add	$ap,$t1,#8*4
Packit c4476c
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]	// result!!!
Packit c4476c
	ldp	$acc0,$acc1,[sp,#8*4]	// t[0..3]
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]	// result!!!
Packit c4476c
	ldp	$acc2,$acc3,[sp,#8*6]
Packit c4476c
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]	// n[0..3]
Packit c4476c
	mov	$tp,sp
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	adds	$np,$np,#8*4		// clear carry bit
Packit c4476c
	mov	$carry,xzr
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Loop_mul4x_reduction:
Packit c4476c
	mul	$t0,$a0,$bi		// lo(a[0..3]*b[4])
Packit c4476c
	adc	$carry,$carry,xzr	// modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$bi
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$bi
Packit c4476c
	and	$cnt,$cnt,#31
Packit c4476c
	mul	$t3,$a3,$bi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$a0,$bi		// hi(a[0..3]*b[4])
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	mul	$mi,$acc0,$n0		// t[0]*n0
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t1,$a1,$bi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t2,$a2,$bi
Packit c4476c
	adc	$acc4,xzr,xzr
Packit c4476c
	umulh	$t3,$a3,$bi
Packit c4476c
	ldr	$bi,[$bp,$cnt]		// next b[i]
Packit c4476c
	adds	$acc1,$acc1,$t0
Packit c4476c
	// (*)	mul	$t0,$m0,$mi
Packit c4476c
	str	$mi,[$tp],#8		// put aside t[0]*n0 for tail processing
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	mul	$t1,$m1,$mi		// lo(n[0..3]*t[0]*n0
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$m2,$mi
Packit c4476c
	adc	$acc4,$acc4,$t3		// can't overflow
Packit c4476c
	mul	$t3,$m3,$mi
Packit c4476c
	// (*)	adds	xzr,$acc0,$t0
Packit c4476c
	subs	xzr,$acc0,#1		// (*)
Packit c4476c
	umulh	$t0,$m0,$mi		// hi(n[0..3]*t[0]*n0
Packit c4476c
	adcs	$acc0,$acc1,$t1
Packit c4476c
	umulh	$t1,$m1,$mi
Packit c4476c
	adcs	$acc1,$acc2,$t2
Packit c4476c
	umulh	$t2,$m2,$mi
Packit c4476c
	adcs	$acc2,$acc3,$t3
Packit c4476c
	umulh	$t3,$m3,$mi
Packit c4476c
	adcs	$acc3,$acc4,$carry
Packit c4476c
	adc	$carry,xzr,xzr
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
	cbnz	$cnt,.Loop_mul4x_reduction
Packit c4476c
Packit c4476c
	adc	$carry,$carry,xzr
Packit c4476c
	ldp	$t0,$t1,[$tp,#8*4]	// t[4..7]
Packit c4476c
	ldp	$t2,$t3,[$tp,#8*6]
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]	// a[4..7]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
Packit c4476c
	ldr	$mi,[sp]		// t[0]*n0
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]	// n[4..7]
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	add	$np,$np,#8*4
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Loop_mul4x_tail:
Packit c4476c
	mul	$t0,$a0,$bi		// lo(a[4..7]*b[4])
Packit c4476c
	adc	$carry,$carry,xzr	// modulo-scheduled
Packit c4476c
	mul	$t1,$a1,$bi
Packit c4476c
	add	$cnt,$cnt,#8
Packit c4476c
	mul	$t2,$a2,$bi
Packit c4476c
	and	$cnt,$cnt,#31
Packit c4476c
	mul	$t3,$a3,$bi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$a0,$bi		// hi(a[4..7]*b[4])
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	umulh	$t1,$a1,$bi
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t2,$a2,$bi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t3,$a3,$bi
Packit c4476c
	adc	$acc4,xzr,xzr
Packit c4476c
	ldr	$bi,[$bp,$cnt]		// next b[i]
Packit c4476c
	adds	$acc1,$acc1,$t0
Packit c4476c
	mul	$t0,$m0,$mi		// lo(n[4..7]*t[0]*n0)
Packit c4476c
	adcs	$acc2,$acc2,$t1
Packit c4476c
	mul	$t1,$m1,$mi
Packit c4476c
	adcs	$acc3,$acc3,$t2
Packit c4476c
	mul	$t2,$m2,$mi
Packit c4476c
	adc	$acc4,$acc4,$t3		// can't overflow
Packit c4476c
	mul	$t3,$m3,$mi
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	umulh	$t0,$m0,$mi		// hi(n[4..7]*t[0]*n0)
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	umulh	$t1,$m1,$mi
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	umulh	$t2,$m2,$mi
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	umulh	$t3,$m3,$mi
Packit c4476c
	adcs	$acc4,$acc4,$carry
Packit c4476c
	ldr	$mi,[sp,$cnt]		// next a[0]*n0
Packit c4476c
	adc	$carry,xzr,xzr
Packit c4476c
	str	$acc0,[$tp],#8		// result!!!
Packit c4476c
	adds	$acc0,$acc1,$t0
Packit c4476c
	sub	$t0,$ap_end,$ap		// done yet?
Packit c4476c
	adcs	$acc1,$acc2,$t1
Packit c4476c
	adcs	$acc2,$acc3,$t2
Packit c4476c
	adcs	$acc3,$acc4,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
	cbnz	$cnt,.Loop_mul4x_tail
Packit c4476c
Packit c4476c
	sub	$t1,$np,$num		// rewinded np?
Packit c4476c
	adc	$carry,$carry,xzr
Packit c4476c
	cbz	$t0,.Loop_mul4x_break
Packit c4476c
Packit c4476c
	ldp	$t0,$t1,[$tp,#8*4]
Packit c4476c
	ldp	$t2,$t3,[$tp,#8*6]
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	adds	$acc0,$acc0,$t0
Packit c4476c
	adcs	$acc1,$acc1,$t1
Packit c4476c
	adcs	$acc2,$acc2,$t2
Packit c4476c
	adcs	$acc3,$acc3,$t3
Packit c4476c
	//adc	$carry,$carry,xzr
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	add	$np,$np,#8*4
Packit c4476c
	b	.Loop_mul4x_tail
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Loop_mul4x_break:
Packit c4476c
	ldp	$t2,$t3,[x29,#96]	// pull rp and &b[num]
Packit c4476c
	adds	$acc0,$acc0,$topmost
Packit c4476c
	add	$bp,$bp,#8*4		// bp++
Packit c4476c
	adcs	$acc1,$acc1,xzr
Packit c4476c
	sub	$ap,$ap,$num		// rewind ap
Packit c4476c
	adcs	$acc2,$acc2,xzr
Packit c4476c
	stp	$acc0,$acc1,[$tp,#8*0]	// result!!!
Packit c4476c
	adcs	$acc3,$acc3,xzr
Packit c4476c
	ldp	$acc0,$acc1,[sp,#8*4]	// t[0..3]
Packit c4476c
	adc	$topmost,$carry,xzr
Packit c4476c
	stp	$acc2,$acc3,[$tp,#8*2]	// result!!!
Packit c4476c
	cmp	$bp,$t3			// done yet?
Packit c4476c
	ldp	$acc2,$acc3,[sp,#8*6]
Packit c4476c
	ldp	$m0,$m1,[$t1,#8*0]	// n[0..3]
Packit c4476c
	ldp	$m2,$m3,[$t1,#8*2]
Packit c4476c
	add	$np,$t1,#8*4
Packit c4476c
	b.eq	.Lmul4x_post
Packit c4476c
Packit c4476c
	ldr	$bi,[$bp]
Packit c4476c
	ldp	$a0,$a1,[$ap,#8*0]	// a[0..3]
Packit c4476c
	ldp	$a2,$a3,[$ap,#8*2]
Packit c4476c
	adds	$ap,$ap,#8*4		// clear carry bit
Packit c4476c
	mov	$carry,xzr
Packit c4476c
	mov	$tp,sp
Packit c4476c
	b	.Loop_mul4x_reduction
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lmul4x_post:
Packit c4476c
	// Final step. We see if result is larger than modulus, and
Packit c4476c
	// if it is, subtract the modulus. But comparison implies
Packit c4476c
	// subtraction. So we subtract modulus, see if it borrowed,
Packit c4476c
	// and conditionally copy original value.
Packit c4476c
	mov	$rp,$t2
Packit c4476c
	mov	$ap_end,$t2		// $rp copy
Packit c4476c
	subs	$t0,$acc0,$m0
Packit c4476c
	add	$tp,sp,#8*8
Packit c4476c
	sbcs	$t1,$acc1,$m1
Packit c4476c
	sub	$cnt,$num,#8*4
Packit c4476c
Packit c4476c
.Lmul4x_sub:
Packit c4476c
	sbcs	$t2,$acc2,$m2
Packit c4476c
	ldp	$m0,$m1,[$np,#8*0]
Packit c4476c
	sub	$cnt,$cnt,#8*4
Packit c4476c
	ldp	$acc0,$acc1,[$tp,#8*0]
Packit c4476c
	sbcs	$t3,$acc3,$m3
Packit c4476c
	ldp	$m2,$m3,[$np,#8*2]
Packit c4476c
	add	$np,$np,#8*4
Packit c4476c
	ldp	$acc2,$acc3,[$tp,#8*2]
Packit c4476c
	add	$tp,$tp,#8*4
Packit c4476c
	stp	$t0,$t1,[$rp,#8*0]
Packit c4476c
	sbcs	$t0,$acc0,$m0
Packit c4476c
	stp	$t2,$t3,[$rp,#8*2]
Packit c4476c
	add	$rp,$rp,#8*4
Packit c4476c
	sbcs	$t1,$acc1,$m1
Packit c4476c
	cbnz	$cnt,.Lmul4x_sub
Packit c4476c
Packit c4476c
	sbcs	$t2,$acc2,$m2
Packit c4476c
	 mov	$tp,sp
Packit c4476c
	 add	$ap,sp,#8*4
Packit c4476c
	 ldp	$a0,$a1,[$ap_end,#8*0]
Packit c4476c
	sbcs	$t3,$acc3,$m3
Packit c4476c
	stp	$t0,$t1,[$rp,#8*0]
Packit c4476c
	 ldp	$a2,$a3,[$ap_end,#8*2]
Packit c4476c
	stp	$t2,$t3,[$rp,#8*2]
Packit c4476c
	 ldp	$acc0,$acc1,[$ap,#8*0]
Packit c4476c
	 ldp	$acc2,$acc3,[$ap,#8*2]
Packit c4476c
	sbcs	xzr,$topmost,xzr	// did it borrow?
Packit c4476c
	ldr	x30,[x29,#8]		// pull return address
Packit c4476c
Packit c4476c
	sub	$cnt,$num,#8*4
Packit c4476c
.Lmul4x_cond_copy:
Packit c4476c
	sub	$cnt,$cnt,#8*4
Packit c4476c
	csel	$t0,$acc0,$a0,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*0]
Packit c4476c
	csel	$t1,$acc1,$a1,lo
Packit c4476c
	ldp	$a0,$a1,[$ap_end,#8*4]
Packit c4476c
	ldp	$acc0,$acc1,[$ap,#8*4]
Packit c4476c
	csel	$t2,$acc2,$a2,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*2]
Packit c4476c
	 add	$tp,$tp,#8*4
Packit c4476c
	csel	$t3,$acc3,$a3,lo
Packit c4476c
	ldp	$a2,$a3,[$ap_end,#8*6]
Packit c4476c
	ldp	$acc2,$acc3,[$ap,#8*6]
Packit c4476c
	add	$ap,$ap,#8*4
Packit c4476c
	stp	$t0,$t1,[$ap_end,#8*0]
Packit c4476c
	stp	$t2,$t3,[$ap_end,#8*2]
Packit c4476c
	add	$ap_end,$ap_end,#8*4
Packit c4476c
	cbnz	$cnt,.Lmul4x_cond_copy
Packit c4476c
Packit c4476c
	csel	$t0,$acc0,$a0,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*0]
Packit c4476c
	csel	$t1,$acc1,$a1,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*2]
Packit c4476c
	csel	$t2,$acc2,$a2,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*3]
Packit c4476c
	csel	$t3,$acc3,$a3,lo
Packit c4476c
	 stp	xzr,xzr,[$tp,#8*4]
Packit c4476c
	stp	$t0,$t1,[$ap_end,#8*0]
Packit c4476c
	stp	$t2,$t3,[$ap_end,#8*2]
Packit c4476c
Packit c4476c
	b	.Lmul4x_done
Packit c4476c
Packit c4476c
.align	4
Packit c4476c
.Lmul4x4_post_condition:
Packit c4476c
	adc	$carry,$carry,xzr
Packit c4476c
	ldr	$ap,[x29,#96]		// pull rp
Packit c4476c
	// $acc0-3,$carry hold result, $m0-7 hold modulus
Packit c4476c
	subs	$a0,$acc0,$m0
Packit c4476c
	ldr	x30,[x29,#8]		// pull return address
Packit c4476c
	sbcs	$a1,$acc1,$m1
Packit c4476c
	 stp	xzr,xzr,[sp,#8*0]
Packit c4476c
	sbcs	$a2,$acc2,$m2
Packit c4476c
	 stp	xzr,xzr,[sp,#8*2]
Packit c4476c
	sbcs	$a3,$acc3,$m3
Packit c4476c
	 stp	xzr,xzr,[sp,#8*4]
Packit c4476c
	sbcs	xzr,$carry,xzr		// did it borrow?
Packit c4476c
	 stp	xzr,xzr,[sp,#8*6]
Packit c4476c
Packit c4476c
	// $a0-3 hold result-modulus
Packit c4476c
	csel	$a0,$acc0,$a0,lo
Packit c4476c
	csel	$a1,$acc1,$a1,lo
Packit c4476c
	csel	$a2,$acc2,$a2,lo
Packit c4476c
	csel	$a3,$acc3,$a3,lo
Packit c4476c
	stp	$a0,$a1,[$ap,#8*0]
Packit c4476c
	stp	$a2,$a3,[$ap,#8*2]
Packit c4476c
Packit c4476c
.Lmul4x_done:
Packit c4476c
	ldp	x19,x20,[x29,#16]
Packit c4476c
	mov	sp,x29
Packit c4476c
	ldp	x21,x22,[x29,#32]
Packit c4476c
	mov	x0,#1
Packit c4476c
	ldp	x23,x24,[x29,#48]
Packit c4476c
	ldp	x25,x26,[x29,#64]
Packit c4476c
	ldp	x27,x28,[x29,#80]
Packit c4476c
	ldr	x29,[sp],#128
Packit c4476c
	.inst	0xd50323bf		// autiasp
Packit c4476c
	ret
Packit c4476c
.size	__bn_mul4x_mont,.-__bn_mul4x_mont
Packit c4476c
___
Packit c4476c
}
Packit c4476c
$code.=<<___;
Packit c4476c
.asciz	"Montgomery Multiplication for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
Packit c4476c
.align	4
Packit c4476c
___
Packit c4476c
Packit c4476c
print $code;
Packit c4476c
Packit c4476c
close STDOUT or die "error closing STDOUT: $!";