77e77ae03fdef3bb54d503f25891932535a94da8
[openssl.git] / crypto / bn / asm / armv8-mont.pl
1 #! /usr/bin/env perl
2 # Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the Apache License 2.0 (the "License").  You may not use
5 # this file except in compliance with the License.  You can obtain a copy
6 # in the file LICENSE in the source distribution or at
7 # https://www.openssl.org/source/license.html
8
9
10 # ====================================================================
11 # Written by Andy Polyakov <appro@openssl.org> for the OpenSSL
12 # project. The module is, however, dual licensed under OpenSSL and
13 # CRYPTOGAMS licenses depending on where you obtain it. For further
14 # details see http://www.openssl.org/~appro/cryptogams/.
15 # ====================================================================
16
17 # March 2015
18 #
19 # "Teaser" Montgomery multiplication module for ARMv8. Needs more
20 # work. While it does improve RSA sign performance by 20-30% (less for
21 # longer keys) on most processors, for some reason RSA2048 is not
22 # faster and RSA4096 goes 15-20% slower on Cortex-A57. Multiplication
23 # instruction issue rate is limited on processor in question, meaning
24 # that dedicated squaring procedure is a must. Well, actually all
25 # contemporary AArch64 processors seem to have limited multiplication
26 # issue rate, i.e. they can't issue multiplication every cycle, which
27 # explains moderate improvement coefficients in comparison to
28 # compiler-generated code. Recall that compiler is instructed to use
29 # umulh and therefore uses same amount of multiplication instructions
30 # to do the job. Assembly's edge is to minimize number of "collateral"
31 # instructions and of course instruction scheduling.
32 #
33 # April 2015
34 #
35 # Squaring procedure that handles lengths divisible by 8 improves
36 # RSA/DSA performance by 25-40-60% depending on processor and key
37 # length. Overall improvement coefficients are always positive in
38 # comparison to compiler-generated code. On Cortex-A57 improvement
39 # is still modest on longest key lengths, while others exhibit e.g.
40 # 50-70% improvement for RSA4096 sign. RSA2048 sign is ~25% faster
41 # on Cortex-A57 and ~60-100% faster on others.
42
43 # $output is the last argument if it looks like a file (it has an extension)
44 # $flavour is the first argument if it doesn't look like a file
45 my $output = $#ARGV >= 0 && $ARGV[$#ARGV] =~ m|\.\w+$| ? pop : undef;
46 my $flavour = $#ARGV >= 0 && $ARGV[0] !~ m|\.| ? shift : undef;
47
48 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
49 ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
50 ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
51 die "can't locate arm-xlate.pl";
52
53 open OUT,"| \"$^X\" $xlate $flavour \"$output\""
54     or die "can't call $xlate: $1";
55 *STDOUT=*OUT;
56
57 ($lo0,$hi0,$aj,$m0,$alo,$ahi,
58  $lo1,$hi1,$nj,$m1,$nlo,$nhi,
59  $ovf, $i,$j,$tp,$tj) = map("x$_",6..17,19..24);
60
61 # int bn_mul_mont(
62 $rp="x0";       # BN_ULONG *rp,
63 $ap="x1";       # const BN_ULONG *ap,
64 $bp="x2";       # const BN_ULONG *bp,
65 $np="x3";       # const BN_ULONG *np,
66 $n0="x4";       # const BN_ULONG *n0,
67 $num="x5";      # int num);
68
69 $code.=<<___;
70 .text
71
72 .globl  bn_mul_mont
73 .type   bn_mul_mont,%function
74 .align  5
75 bn_mul_mont:
76         tst     $num,#7
77         b.eq    __bn_sqr8x_mont
78         tst     $num,#3
79         b.eq    __bn_mul4x_mont
80 .Lmul_mont:
81         stp     x29,x30,[sp,#-64]!
82         add     x29,sp,#0
83         stp     x19,x20,[sp,#16]
84         stp     x21,x22,[sp,#32]
85         stp     x23,x24,[sp,#48]
86
87         ldr     $m0,[$bp],#8            // bp[0]
88         sub     $tp,sp,$num,lsl#3
89         ldp     $hi0,$aj,[$ap],#16      // ap[0..1]
90         lsl     $num,$num,#3
91         ldr     $n0,[$n0]               // *n0
92         and     $tp,$tp,#-16            // ABI says so
93         ldp     $hi1,$nj,[$np],#16      // np[0..1]
94
95         mul     $lo0,$hi0,$m0           // ap[0]*bp[0]
96         sub     $j,$num,#16             // j=num-2
97         umulh   $hi0,$hi0,$m0
98         mul     $alo,$aj,$m0            // ap[1]*bp[0]
99         umulh   $ahi,$aj,$m0
100
101         mul     $m1,$lo0,$n0            // "tp[0]"*n0
102         mov     sp,$tp                  // alloca
103
104         // (*)  mul     $lo1,$hi1,$m1   // np[0]*m1
105         umulh   $hi1,$hi1,$m1
106         mul     $nlo,$nj,$m1            // np[1]*m1
107         // (*)  adds    $lo1,$lo1,$lo0  // discarded
108         // (*)  As for removal of first multiplication and addition
109         //      instructions. The outcome of first addition is
110         //      guaranteed to be zero, which leaves two computationally
111         //      significant outcomes: it either carries or not. Then
112         //      question is when does it carry? Is there alternative
113         //      way to deduce it? If you follow operations, you can
114         //      observe that condition for carry is quite simple:
115         //      $lo0 being non-zero. So that carry can be calculated
116         //      by adding -1 to $lo0. That's what next instruction does.
117         subs    xzr,$lo0,#1             // (*)
118         umulh   $nhi,$nj,$m1
119         adc     $hi1,$hi1,xzr
120         cbz     $j,.L1st_skip
121
122 .L1st:
123         ldr     $aj,[$ap],#8
124         adds    $lo0,$alo,$hi0
125         sub     $j,$j,#8                // j--
126         adc     $hi0,$ahi,xzr
127
128         ldr     $nj,[$np],#8
129         adds    $lo1,$nlo,$hi1
130         mul     $alo,$aj,$m0            // ap[j]*bp[0]
131         adc     $hi1,$nhi,xzr
132         umulh   $ahi,$aj,$m0
133
134         adds    $lo1,$lo1,$lo0
135         mul     $nlo,$nj,$m1            // np[j]*m1
136         adc     $hi1,$hi1,xzr
137         umulh   $nhi,$nj,$m1
138         str     $lo1,[$tp],#8           // tp[j-1]
139         cbnz    $j,.L1st
140
141 .L1st_skip:
142         adds    $lo0,$alo,$hi0
143         sub     $ap,$ap,$num            // rewind $ap
144         adc     $hi0,$ahi,xzr
145
146         adds    $lo1,$nlo,$hi1
147         sub     $np,$np,$num            // rewind $np
148         adc     $hi1,$nhi,xzr
149
150         adds    $lo1,$lo1,$lo0
151         sub     $i,$num,#8              // i=num-1
152         adcs    $hi1,$hi1,$hi0
153
154         adc     $ovf,xzr,xzr            // upmost overflow bit
155         stp     $lo1,$hi1,[$tp]
156
157 .Louter:
158         ldr     $m0,[$bp],#8            // bp[i]
159         ldp     $hi0,$aj,[$ap],#16
160         ldr     $tj,[sp]                // tp[0]
161         add     $tp,sp,#8
162
163         mul     $lo0,$hi0,$m0           // ap[0]*bp[i]
164         sub     $j,$num,#16             // j=num-2
165         umulh   $hi0,$hi0,$m0
166         ldp     $hi1,$nj,[$np],#16
167         mul     $alo,$aj,$m0            // ap[1]*bp[i]
168         adds    $lo0,$lo0,$tj
169         umulh   $ahi,$aj,$m0
170         adc     $hi0,$hi0,xzr
171
172         mul     $m1,$lo0,$n0
173         sub     $i,$i,#8                // i--
174
175         // (*)  mul     $lo1,$hi1,$m1   // np[0]*m1
176         umulh   $hi1,$hi1,$m1
177         mul     $nlo,$nj,$m1            // np[1]*m1
178         // (*)  adds    $lo1,$lo1,$lo0
179         subs    xzr,$lo0,#1             // (*)
180         umulh   $nhi,$nj,$m1
181         cbz     $j,.Linner_skip
182
183 .Linner:
184         ldr     $aj,[$ap],#8
185         adc     $hi1,$hi1,xzr
186         ldr     $tj,[$tp],#8            // tp[j]
187         adds    $lo0,$alo,$hi0
188         sub     $j,$j,#8                // j--
189         adc     $hi0,$ahi,xzr
190
191         adds    $lo1,$nlo,$hi1
192         ldr     $nj,[$np],#8
193         adc     $hi1,$nhi,xzr
194
195         mul     $alo,$aj,$m0            // ap[j]*bp[i]
196         adds    $lo0,$lo0,$tj
197         umulh   $ahi,$aj,$m0
198         adc     $hi0,$hi0,xzr
199
200         mul     $nlo,$nj,$m1            // np[j]*m1
201         adds    $lo1,$lo1,$lo0
202         umulh   $nhi,$nj,$m1
203         stur    $lo1,[$tp,#-16]         // tp[j-1]
204         cbnz    $j,.Linner
205
206 .Linner_skip:
207         ldr     $tj,[$tp],#8            // tp[j]
208         adc     $hi1,$hi1,xzr
209         adds    $lo0,$alo,$hi0
210         sub     $ap,$ap,$num            // rewind $ap
211         adc     $hi0,$ahi,xzr
212
213         adds    $lo1,$nlo,$hi1
214         sub     $np,$np,$num            // rewind $np
215         adcs    $hi1,$nhi,$ovf
216         adc     $ovf,xzr,xzr
217
218         adds    $lo0,$lo0,$tj
219         adc     $hi0,$hi0,xzr
220
221         adds    $lo1,$lo1,$lo0
222         adcs    $hi1,$hi1,$hi0
223         adc     $ovf,$ovf,xzr           // upmost overflow bit
224         stp     $lo1,$hi1,[$tp,#-16]
225
226         cbnz    $i,.Louter
227
228         // Final step. We see if result is larger than modulus, and
229         // if it is, subtract the modulus. But comparison implies
230         // subtraction. So we subtract modulus, see if it borrowed,
231         // and conditionally copy original value.
232         ldr     $tj,[sp]                // tp[0]
233         add     $tp,sp,#8
234         ldr     $nj,[$np],#8            // np[0]
235         subs    $j,$num,#8              // j=num-1 and clear borrow
236         mov     $ap,$rp
237 .Lsub:
238         sbcs    $aj,$tj,$nj             // tp[j]-np[j]
239         ldr     $tj,[$tp],#8
240         sub     $j,$j,#8                // j--
241         ldr     $nj,[$np],#8
242         str     $aj,[$ap],#8            // rp[j]=tp[j]-np[j]
243         cbnz    $j,.Lsub
244
245         sbcs    $aj,$tj,$nj
246         sbcs    $ovf,$ovf,xzr           // did it borrow?
247         str     $aj,[$ap],#8            // rp[num-1]
248
249         ldr     $tj,[sp]                // tp[0]
250         add     $tp,sp,#8
251         ldr     $aj,[$rp],#8            // rp[0]
252         sub     $num,$num,#8            // num--
253         nop
254 .Lcond_copy:
255         sub     $num,$num,#8            // num--
256         csel    $nj,$tj,$aj,lo          // did it borrow?
257         ldr     $tj,[$tp],#8
258         ldr     $aj,[$rp],#8
259         stur    xzr,[$tp,#-16]          // wipe tp
260         stur    $nj,[$rp,#-16]
261         cbnz    $num,.Lcond_copy
262
263         csel    $nj,$tj,$aj,lo
264         stur    xzr,[$tp,#-8]           // wipe tp
265         stur    $nj,[$rp,#-8]
266
267         ldp     x19,x20,[x29,#16]
268         mov     sp,x29
269         ldp     x21,x22,[x29,#32]
270         mov     x0,#1
271         ldp     x23,x24,[x29,#48]
272         ldr     x29,[sp],#64
273         ret
274 .size   bn_mul_mont,.-bn_mul_mont
275 ___
276 {
277 ########################################################################
278 # Following is ARMv8 adaptation of sqrx8x_mont from x86_64-mont5 module.
279
280 my ($a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7)=map("x$_",(6..13));
281 my ($t0,$t1,$t2,$t3)=map("x$_",(14..17));
282 my ($acc0,$acc1,$acc2,$acc3,$acc4,$acc5,$acc6,$acc7)=map("x$_",(19..26));
283 my ($cnt,$carry,$topmost)=("x27","x28","x30");
284 my ($tp,$ap_end,$na0)=($bp,$np,$carry);
285
286 $code.=<<___;
287 .type   __bn_sqr8x_mont,%function
288 .align  5
289 __bn_sqr8x_mont:
290         cmp     $ap,$bp
291         b.ne    __bn_mul4x_mont
292 .Lsqr8x_mont:
293         .inst   0xd503233f              // paciasp
294         stp     x29,x30,[sp,#-128]!
295         add     x29,sp,#0
296         stp     x19,x20,[sp,#16]
297         stp     x21,x22,[sp,#32]
298         stp     x23,x24,[sp,#48]
299         stp     x25,x26,[sp,#64]
300         stp     x27,x28,[sp,#80]
301         stp     $rp,$np,[sp,#96]        // offload rp and np
302
303         ldp     $a0,$a1,[$ap,#8*0]
304         ldp     $a2,$a3,[$ap,#8*2]
305         ldp     $a4,$a5,[$ap,#8*4]
306         ldp     $a6,$a7,[$ap,#8*6]
307
308         sub     $tp,sp,$num,lsl#4
309         lsl     $num,$num,#3
310         ldr     $n0,[$n0]               // *n0
311         mov     sp,$tp                  // alloca
312         sub     $cnt,$num,#8*8
313         b       .Lsqr8x_zero_start
314
315 .Lsqr8x_zero:
316         sub     $cnt,$cnt,#8*8
317         stp     xzr,xzr,[$tp,#8*0]
318         stp     xzr,xzr,[$tp,#8*2]
319         stp     xzr,xzr,[$tp,#8*4]
320         stp     xzr,xzr,[$tp,#8*6]
321 .Lsqr8x_zero_start:
322         stp     xzr,xzr,[$tp,#8*8]
323         stp     xzr,xzr,[$tp,#8*10]
324         stp     xzr,xzr,[$tp,#8*12]
325         stp     xzr,xzr,[$tp,#8*14]
326         add     $tp,$tp,#8*16
327         cbnz    $cnt,.Lsqr8x_zero
328
329         add     $ap_end,$ap,$num
330         add     $ap,$ap,#8*8
331         mov     $acc0,xzr
332         mov     $acc1,xzr
333         mov     $acc2,xzr
334         mov     $acc3,xzr
335         mov     $acc4,xzr
336         mov     $acc5,xzr
337         mov     $acc6,xzr
338         mov     $acc7,xzr
339         mov     $tp,sp
340         str     $n0,[x29,#112]          // offload n0
341
342         // Multiply everything but a[i]*a[i]
343 .align  4
344 .Lsqr8x_outer_loop:
345         //                                                 a[1]a[0]     (i)
346         //                                             a[2]a[0]
347         //                                         a[3]a[0]
348         //                                     a[4]a[0]
349         //                                 a[5]a[0]
350         //                             a[6]a[0]
351         //                         a[7]a[0]
352         //                                         a[2]a[1]             (ii)
353         //                                     a[3]a[1]
354         //                                 a[4]a[1]
355         //                             a[5]a[1]
356         //                         a[6]a[1]
357         //                     a[7]a[1]
358         //                                 a[3]a[2]                     (iii)
359         //                             a[4]a[2]
360         //                         a[5]a[2]
361         //                     a[6]a[2]
362         //                 a[7]a[2]
363         //                         a[4]a[3]                             (iv)
364         //                     a[5]a[3]
365         //                 a[6]a[3]
366         //             a[7]a[3]
367         //                 a[5]a[4]                                     (v)
368         //             a[6]a[4]
369         //         a[7]a[4]
370         //         a[6]a[5]                                             (vi)
371         //     a[7]a[5]
372         // a[7]a[6]                                                     (vii)
373
374         mul     $t0,$a1,$a0             // lo(a[1..7]*a[0])             (i)
375         mul     $t1,$a2,$a0
376         mul     $t2,$a3,$a0
377         mul     $t3,$a4,$a0
378         adds    $acc1,$acc1,$t0         // t[1]+lo(a[1]*a[0])
379         mul     $t0,$a5,$a0
380         adcs    $acc2,$acc2,$t1
381         mul     $t1,$a6,$a0
382         adcs    $acc3,$acc3,$t2
383         mul     $t2,$a7,$a0
384         adcs    $acc4,$acc4,$t3
385         umulh   $t3,$a1,$a0             // hi(a[1..7]*a[0])
386         adcs    $acc5,$acc5,$t0
387         umulh   $t0,$a2,$a0
388         adcs    $acc6,$acc6,$t1
389         umulh   $t1,$a3,$a0
390         adcs    $acc7,$acc7,$t2
391         umulh   $t2,$a4,$a0
392         stp     $acc0,$acc1,[$tp],#8*2  // t[0..1]
393         adc     $acc0,xzr,xzr           // t[8]
394         adds    $acc2,$acc2,$t3         // t[2]+lo(a[1]*a[0])
395         umulh   $t3,$a5,$a0
396         adcs    $acc3,$acc3,$t0
397         umulh   $t0,$a6,$a0
398         adcs    $acc4,$acc4,$t1
399         umulh   $t1,$a7,$a0
400         adcs    $acc5,$acc5,$t2
401          mul    $t2,$a2,$a1             // lo(a[2..7]*a[1])             (ii)
402         adcs    $acc6,$acc6,$t3
403          mul    $t3,$a3,$a1
404         adcs    $acc7,$acc7,$t0
405          mul    $t0,$a4,$a1
406         adc     $acc0,$acc0,$t1
407
408         mul     $t1,$a5,$a1
409         adds    $acc3,$acc3,$t2
410         mul     $t2,$a6,$a1
411         adcs    $acc4,$acc4,$t3
412         mul     $t3,$a7,$a1
413         adcs    $acc5,$acc5,$t0
414         umulh   $t0,$a2,$a1             // hi(a[2..7]*a[1])
415         adcs    $acc6,$acc6,$t1
416         umulh   $t1,$a3,$a1
417         adcs    $acc7,$acc7,$t2
418         umulh   $t2,$a4,$a1
419         adcs    $acc0,$acc0,$t3
420         umulh   $t3,$a5,$a1
421         stp     $acc2,$acc3,[$tp],#8*2  // t[2..3]
422         adc     $acc1,xzr,xzr           // t[9]
423         adds    $acc4,$acc4,$t0
424         umulh   $t0,$a6,$a1
425         adcs    $acc5,$acc5,$t1
426         umulh   $t1,$a7,$a1
427         adcs    $acc6,$acc6,$t2
428          mul    $t2,$a3,$a2             // lo(a[3..7]*a[2])             (iii)
429         adcs    $acc7,$acc7,$t3
430          mul    $t3,$a4,$a2
431         adcs    $acc0,$acc0,$t0
432          mul    $t0,$a5,$a2
433         adc     $acc1,$acc1,$t1
434
435         mul     $t1,$a6,$a2
436         adds    $acc5,$acc5,$t2
437         mul     $t2,$a7,$a2
438         adcs    $acc6,$acc6,$t3
439         umulh   $t3,$a3,$a2             // hi(a[3..7]*a[2])
440         adcs    $acc7,$acc7,$t0
441         umulh   $t0,$a4,$a2
442         adcs    $acc0,$acc0,$t1
443         umulh   $t1,$a5,$a2
444         adcs    $acc1,$acc1,$t2
445         umulh   $t2,$a6,$a2
446         stp     $acc4,$acc5,[$tp],#8*2  // t[4..5]
447         adc     $acc2,xzr,xzr           // t[10]
448         adds    $acc6,$acc6,$t3
449         umulh   $t3,$a7,$a2
450         adcs    $acc7,$acc7,$t0
451          mul    $t0,$a4,$a3             // lo(a[4..7]*a[3])             (iv)
452         adcs    $acc0,$acc0,$t1
453          mul    $t1,$a5,$a3
454         adcs    $acc1,$acc1,$t2
455          mul    $t2,$a6,$a3
456         adc     $acc2,$acc2,$t3
457
458         mul     $t3,$a7,$a3
459         adds    $acc7,$acc7,$t0
460         umulh   $t0,$a4,$a3             // hi(a[4..7]*a[3])
461         adcs    $acc0,$acc0,$t1
462         umulh   $t1,$a5,$a3
463         adcs    $acc1,$acc1,$t2
464         umulh   $t2,$a6,$a3
465         adcs    $acc2,$acc2,$t3
466         umulh   $t3,$a7,$a3
467         stp     $acc6,$acc7,[$tp],#8*2  // t[6..7]
468         adc     $acc3,xzr,xzr           // t[11]
469         adds    $acc0,$acc0,$t0
470          mul    $t0,$a5,$a4             // lo(a[5..7]*a[4])             (v)
471         adcs    $acc1,$acc1,$t1
472          mul    $t1,$a6,$a4
473         adcs    $acc2,$acc2,$t2
474          mul    $t2,$a7,$a4
475         adc     $acc3,$acc3,$t3
476
477         umulh   $t3,$a5,$a4             // hi(a[5..7]*a[4])
478         adds    $acc1,$acc1,$t0
479         umulh   $t0,$a6,$a4
480         adcs    $acc2,$acc2,$t1
481         umulh   $t1,$a7,$a4
482         adcs    $acc3,$acc3,$t2
483          mul    $t2,$a6,$a5             // lo(a[6..7]*a[5])             (vi)
484         adc     $acc4,xzr,xzr           // t[12]
485         adds    $acc2,$acc2,$t3
486          mul    $t3,$a7,$a5
487         adcs    $acc3,$acc3,$t0
488          umulh  $t0,$a6,$a5             // hi(a[6..7]*a[5])
489         adc     $acc4,$acc4,$t1
490
491         umulh   $t1,$a7,$a5
492         adds    $acc3,$acc3,$t2
493          mul    $t2,$a7,$a6             // lo(a[7]*a[6])                (vii)
494         adcs    $acc4,$acc4,$t3
495          umulh  $t3,$a7,$a6             // hi(a[7]*a[6])
496         adc     $acc5,xzr,xzr           // t[13]
497         adds    $acc4,$acc4,$t0
498         sub     $cnt,$ap_end,$ap        // done yet?
499         adc     $acc5,$acc5,$t1
500
501         adds    $acc5,$acc5,$t2
502         sub     $t0,$ap_end,$num        // rewinded ap
503         adc     $acc6,xzr,xzr           // t[14]
504         add     $acc6,$acc6,$t3
505
506         cbz     $cnt,.Lsqr8x_outer_break
507
508         mov     $n0,$a0
509         ldp     $a0,$a1,[$tp,#8*0]
510         ldp     $a2,$a3,[$tp,#8*2]
511         ldp     $a4,$a5,[$tp,#8*4]
512         ldp     $a6,$a7,[$tp,#8*6]
513         adds    $acc0,$acc0,$a0
514         adcs    $acc1,$acc1,$a1
515         ldp     $a0,$a1,[$ap,#8*0]
516         adcs    $acc2,$acc2,$a2
517         adcs    $acc3,$acc3,$a3
518         ldp     $a2,$a3,[$ap,#8*2]
519         adcs    $acc4,$acc4,$a4
520         adcs    $acc5,$acc5,$a5
521         ldp     $a4,$a5,[$ap,#8*4]
522         adcs    $acc6,$acc6,$a6
523         mov     $rp,$ap
524         adcs    $acc7,xzr,$a7
525         ldp     $a6,$a7,[$ap,#8*6]
526         add     $ap,$ap,#8*8
527         //adc   $carry,xzr,xzr          // moved below
528         mov     $cnt,#-8*8
529
530         //                                                         a[8]a[0]
531         //                                                     a[9]a[0]
532         //                                                 a[a]a[0]
533         //                                             a[b]a[0]
534         //                                         a[c]a[0]
535         //                                     a[d]a[0]
536         //                                 a[e]a[0]
537         //                             a[f]a[0]
538         //                                                     a[8]a[1]
539         //                         a[f]a[1]........................
540         //                                                 a[8]a[2]
541         //                     a[f]a[2]........................
542         //                                             a[8]a[3]
543         //                 a[f]a[3]........................
544         //                                         a[8]a[4]
545         //             a[f]a[4]........................
546         //                                     a[8]a[5]
547         //         a[f]a[5]........................
548         //                                 a[8]a[6]
549         //     a[f]a[6]........................
550         //                             a[8]a[7]
551         // a[f]a[7]........................
552 .Lsqr8x_mul:
553         mul     $t0,$a0,$n0
554         adc     $carry,xzr,xzr          // carry bit, modulo-scheduled
555         mul     $t1,$a1,$n0
556         add     $cnt,$cnt,#8
557         mul     $t2,$a2,$n0
558         mul     $t3,$a3,$n0
559         adds    $acc0,$acc0,$t0
560         mul     $t0,$a4,$n0
561         adcs    $acc1,$acc1,$t1
562         mul     $t1,$a5,$n0
563         adcs    $acc2,$acc2,$t2
564         mul     $t2,$a6,$n0
565         adcs    $acc3,$acc3,$t3
566         mul     $t3,$a7,$n0
567         adcs    $acc4,$acc4,$t0
568         umulh   $t0,$a0,$n0
569         adcs    $acc5,$acc5,$t1
570         umulh   $t1,$a1,$n0
571         adcs    $acc6,$acc6,$t2
572         umulh   $t2,$a2,$n0
573         adcs    $acc7,$acc7,$t3
574         umulh   $t3,$a3,$n0
575         adc     $carry,$carry,xzr
576         str     $acc0,[$tp],#8
577         adds    $acc0,$acc1,$t0
578         umulh   $t0,$a4,$n0
579         adcs    $acc1,$acc2,$t1
580         umulh   $t1,$a5,$n0
581         adcs    $acc2,$acc3,$t2
582         umulh   $t2,$a6,$n0
583         adcs    $acc3,$acc4,$t3
584         umulh   $t3,$a7,$n0
585         ldr     $n0,[$rp,$cnt]
586         adcs    $acc4,$acc5,$t0
587         adcs    $acc5,$acc6,$t1
588         adcs    $acc6,$acc7,$t2
589         adcs    $acc7,$carry,$t3
590         //adc   $carry,xzr,xzr          // moved above
591         cbnz    $cnt,.Lsqr8x_mul
592                                         // note that carry flag is guaranteed
593                                         // to be zero at this point
594         cmp     $ap,$ap_end             // done yet?
595         b.eq    .Lsqr8x_break
596
597         ldp     $a0,$a1,[$tp,#8*0]
598         ldp     $a2,$a3,[$tp,#8*2]
599         ldp     $a4,$a5,[$tp,#8*4]
600         ldp     $a6,$a7,[$tp,#8*6]
601         adds    $acc0,$acc0,$a0
602         ldur    $n0,[$rp,#-8*8]
603         adcs    $acc1,$acc1,$a1
604         ldp     $a0,$a1,[$ap,#8*0]
605         adcs    $acc2,$acc2,$a2
606         adcs    $acc3,$acc3,$a3
607         ldp     $a2,$a3,[$ap,#8*2]
608         adcs    $acc4,$acc4,$a4
609         adcs    $acc5,$acc5,$a5
610         ldp     $a4,$a5,[$ap,#8*4]
611         adcs    $acc6,$acc6,$a6
612         mov     $cnt,#-8*8
613         adcs    $acc7,$acc7,$a7
614         ldp     $a6,$a7,[$ap,#8*6]
615         add     $ap,$ap,#8*8
616         //adc   $carry,xzr,xzr          // moved above
617         b       .Lsqr8x_mul
618
619 .align  4
620 .Lsqr8x_break:
621         ldp     $a0,$a1,[$rp,#8*0]
622         add     $ap,$rp,#8*8
623         ldp     $a2,$a3,[$rp,#8*2]
624         sub     $t0,$ap_end,$ap         // is it last iteration?
625         ldp     $a4,$a5,[$rp,#8*4]
626         sub     $t1,$tp,$t0
627         ldp     $a6,$a7,[$rp,#8*6]
628         cbz     $t0,.Lsqr8x_outer_loop
629
630         stp     $acc0,$acc1,[$tp,#8*0]
631         ldp     $acc0,$acc1,[$t1,#8*0]
632         stp     $acc2,$acc3,[$tp,#8*2]
633         ldp     $acc2,$acc3,[$t1,#8*2]
634         stp     $acc4,$acc5,[$tp,#8*4]
635         ldp     $acc4,$acc5,[$t1,#8*4]
636         stp     $acc6,$acc7,[$tp,#8*6]
637         mov     $tp,$t1
638         ldp     $acc6,$acc7,[$t1,#8*6]
639         b       .Lsqr8x_outer_loop
640
641 .align  4
642 .Lsqr8x_outer_break:
643         // Now multiply above result by 2 and add a[n-1]*a[n-1]|...|a[0]*a[0]
644         ldp     $a1,$a3,[$t0,#8*0]      // recall that $t0 is &a[0]
645         ldp     $t1,$t2,[sp,#8*1]
646         ldp     $a5,$a7,[$t0,#8*2]
647         add     $ap,$t0,#8*4
648         ldp     $t3,$t0,[sp,#8*3]
649
650         stp     $acc0,$acc1,[$tp,#8*0]
651         mul     $acc0,$a1,$a1
652         stp     $acc2,$acc3,[$tp,#8*2]
653         umulh   $a1,$a1,$a1
654         stp     $acc4,$acc5,[$tp,#8*4]
655         mul     $a2,$a3,$a3
656         stp     $acc6,$acc7,[$tp,#8*6]
657         mov     $tp,sp
658         umulh   $a3,$a3,$a3
659         adds    $acc1,$a1,$t1,lsl#1
660         extr    $t1,$t2,$t1,#63
661         sub     $cnt,$num,#8*4
662
663 .Lsqr4x_shift_n_add:
664         adcs    $acc2,$a2,$t1
665         extr    $t2,$t3,$t2,#63
666         sub     $cnt,$cnt,#8*4
667         adcs    $acc3,$a3,$t2
668         ldp     $t1,$t2,[$tp,#8*5]
669         mul     $a4,$a5,$a5
670         ldp     $a1,$a3,[$ap],#8*2
671         umulh   $a5,$a5,$a5
672         mul     $a6,$a7,$a7
673         umulh   $a7,$a7,$a7
674         extr    $t3,$t0,$t3,#63
675         stp     $acc0,$acc1,[$tp,#8*0]
676         adcs    $acc4,$a4,$t3
677         extr    $t0,$t1,$t0,#63
678         stp     $acc2,$acc3,[$tp,#8*2]
679         adcs    $acc5,$a5,$t0
680         ldp     $t3,$t0,[$tp,#8*7]
681         extr    $t1,$t2,$t1,#63
682         adcs    $acc6,$a6,$t1
683         extr    $t2,$t3,$t2,#63
684         adcs    $acc7,$a7,$t2
685         ldp     $t1,$t2,[$tp,#8*9]
686         mul     $a0,$a1,$a1
687         ldp     $a5,$a7,[$ap],#8*2
688         umulh   $a1,$a1,$a1
689         mul     $a2,$a3,$a3
690         umulh   $a3,$a3,$a3
691         stp     $acc4,$acc5,[$tp,#8*4]
692         extr    $t3,$t0,$t3,#63
693         stp     $acc6,$acc7,[$tp,#8*6]
694         add     $tp,$tp,#8*8
695         adcs    $acc0,$a0,$t3
696         extr    $t0,$t1,$t0,#63
697         adcs    $acc1,$a1,$t0
698         ldp     $t3,$t0,[$tp,#8*3]
699         extr    $t1,$t2,$t1,#63
700         cbnz    $cnt,.Lsqr4x_shift_n_add
701 ___
702 my ($np,$np_end)=($ap,$ap_end);
703 $code.=<<___;
704          ldp    $np,$n0,[x29,#104]      // pull np and n0
705
706         adcs    $acc2,$a2,$t1
707         extr    $t2,$t3,$t2,#63
708         adcs    $acc3,$a3,$t2
709         ldp     $t1,$t2,[$tp,#8*5]
710         mul     $a4,$a5,$a5
711         umulh   $a5,$a5,$a5
712         stp     $acc0,$acc1,[$tp,#8*0]
713         mul     $a6,$a7,$a7
714         umulh   $a7,$a7,$a7
715         stp     $acc2,$acc3,[$tp,#8*2]
716         extr    $t3,$t0,$t3,#63
717         adcs    $acc4,$a4,$t3
718         extr    $t0,$t1,$t0,#63
719          ldp    $acc0,$acc1,[sp,#8*0]
720         adcs    $acc5,$a5,$t0
721         extr    $t1,$t2,$t1,#63
722          ldp    $a0,$a1,[$np,#8*0]
723         adcs    $acc6,$a6,$t1
724         extr    $t2,xzr,$t2,#63
725          ldp    $a2,$a3,[$np,#8*2]
726         adc     $acc7,$a7,$t2
727          ldp    $a4,$a5,[$np,#8*4]
728
729         // Reduce by 512 bits per iteration
730         mul     $na0,$n0,$acc0          // t[0]*n0
731         ldp     $a6,$a7,[$np,#8*6]
732         add     $np_end,$np,$num
733         ldp     $acc2,$acc3,[sp,#8*2]
734         stp     $acc4,$acc5,[$tp,#8*4]
735         ldp     $acc4,$acc5,[sp,#8*4]
736         stp     $acc6,$acc7,[$tp,#8*6]
737         ldp     $acc6,$acc7,[sp,#8*6]
738         add     $np,$np,#8*8
739         mov     $topmost,xzr            // initial top-most carry
740         mov     $tp,sp
741         mov     $cnt,#8
742
743 .Lsqr8x_reduction:
744         // (*)  mul     $t0,$a0,$na0    // lo(n[0-7])*lo(t[0]*n0)
745         mul     $t1,$a1,$na0
746         sub     $cnt,$cnt,#1
747         mul     $t2,$a2,$na0
748         str     $na0,[$tp],#8           // put aside t[0]*n0 for tail processing
749         mul     $t3,$a3,$na0
750         // (*)  adds    xzr,$acc0,$t0
751         subs    xzr,$acc0,#1            // (*)
752         mul     $t0,$a4,$na0
753         adcs    $acc0,$acc1,$t1
754         mul     $t1,$a5,$na0
755         adcs    $acc1,$acc2,$t2
756         mul     $t2,$a6,$na0
757         adcs    $acc2,$acc3,$t3
758         mul     $t3,$a7,$na0
759         adcs    $acc3,$acc4,$t0
760         umulh   $t0,$a0,$na0            // hi(n[0-7])*lo(t[0]*n0)
761         adcs    $acc4,$acc5,$t1
762         umulh   $t1,$a1,$na0
763         adcs    $acc5,$acc6,$t2
764         umulh   $t2,$a2,$na0
765         adcs    $acc6,$acc7,$t3
766         umulh   $t3,$a3,$na0
767         adc     $acc7,xzr,xzr
768         adds    $acc0,$acc0,$t0
769         umulh   $t0,$a4,$na0
770         adcs    $acc1,$acc1,$t1
771         umulh   $t1,$a5,$na0
772         adcs    $acc2,$acc2,$t2
773         umulh   $t2,$a6,$na0
774         adcs    $acc3,$acc3,$t3
775         umulh   $t3,$a7,$na0
776         mul     $na0,$n0,$acc0          // next t[0]*n0
777         adcs    $acc4,$acc4,$t0
778         adcs    $acc5,$acc5,$t1
779         adcs    $acc6,$acc6,$t2
780         adc     $acc7,$acc7,$t3
781         cbnz    $cnt,.Lsqr8x_reduction
782
783         ldp     $t0,$t1,[$tp,#8*0]
784         ldp     $t2,$t3,[$tp,#8*2]
785         mov     $rp,$tp
786         sub     $cnt,$np_end,$np        // done yet?
787         adds    $acc0,$acc0,$t0
788         adcs    $acc1,$acc1,$t1
789         ldp     $t0,$t1,[$tp,#8*4]
790         adcs    $acc2,$acc2,$t2
791         adcs    $acc3,$acc3,$t3
792         ldp     $t2,$t3,[$tp,#8*6]
793         adcs    $acc4,$acc4,$t0
794         adcs    $acc5,$acc5,$t1
795         adcs    $acc6,$acc6,$t2
796         adcs    $acc7,$acc7,$t3
797         //adc   $carry,xzr,xzr          // moved below
798         cbz     $cnt,.Lsqr8x8_post_condition
799
800         ldur    $n0,[$tp,#-8*8]
801         ldp     $a0,$a1,[$np,#8*0]
802         ldp     $a2,$a3,[$np,#8*2]
803         ldp     $a4,$a5,[$np,#8*4]
804         mov     $cnt,#-8*8
805         ldp     $a6,$a7,[$np,#8*6]
806         add     $np,$np,#8*8
807
808 .Lsqr8x_tail:
809         mul     $t0,$a0,$n0
810         adc     $carry,xzr,xzr          // carry bit, modulo-scheduled
811         mul     $t1,$a1,$n0
812         add     $cnt,$cnt,#8
813         mul     $t2,$a2,$n0
814         mul     $t3,$a3,$n0
815         adds    $acc0,$acc0,$t0
816         mul     $t0,$a4,$n0
817         adcs    $acc1,$acc1,$t1
818         mul     $t1,$a5,$n0
819         adcs    $acc2,$acc2,$t2
820         mul     $t2,$a6,$n0
821         adcs    $acc3,$acc3,$t3
822         mul     $t3,$a7,$n0
823         adcs    $acc4,$acc4,$t0
824         umulh   $t0,$a0,$n0
825         adcs    $acc5,$acc5,$t1
826         umulh   $t1,$a1,$n0
827         adcs    $acc6,$acc6,$t2
828         umulh   $t2,$a2,$n0
829         adcs    $acc7,$acc7,$t3
830         umulh   $t3,$a3,$n0
831         adc     $carry,$carry,xzr
832         str     $acc0,[$tp],#8
833         adds    $acc0,$acc1,$t0
834         umulh   $t0,$a4,$n0
835         adcs    $acc1,$acc2,$t1
836         umulh   $t1,$a5,$n0
837         adcs    $acc2,$acc3,$t2
838         umulh   $t2,$a6,$n0
839         adcs    $acc3,$acc4,$t3
840         umulh   $t3,$a7,$n0
841         ldr     $n0,[$rp,$cnt]
842         adcs    $acc4,$acc5,$t0
843         adcs    $acc5,$acc6,$t1
844         adcs    $acc6,$acc7,$t2
845         adcs    $acc7,$carry,$t3
846         //adc   $carry,xzr,xzr          // moved above
847         cbnz    $cnt,.Lsqr8x_tail
848                                         // note that carry flag is guaranteed
849                                         // to be zero at this point
850         ldp     $a0,$a1,[$tp,#8*0]
851         sub     $cnt,$np_end,$np        // done yet?
852         sub     $t2,$np_end,$num        // rewinded np
853         ldp     $a2,$a3,[$tp,#8*2]
854         ldp     $a4,$a5,[$tp,#8*4]
855         ldp     $a6,$a7,[$tp,#8*6]
856         cbz     $cnt,.Lsqr8x_tail_break
857
858         ldur    $n0,[$rp,#-8*8]
859         adds    $acc0,$acc0,$a0
860         adcs    $acc1,$acc1,$a1
861         ldp     $a0,$a1,[$np,#8*0]
862         adcs    $acc2,$acc2,$a2
863         adcs    $acc3,$acc3,$a3
864         ldp     $a2,$a3,[$np,#8*2]
865         adcs    $acc4,$acc4,$a4
866         adcs    $acc5,$acc5,$a5
867         ldp     $a4,$a5,[$np,#8*4]
868         adcs    $acc6,$acc6,$a6
869         mov     $cnt,#-8*8
870         adcs    $acc7,$acc7,$a7
871         ldp     $a6,$a7,[$np,#8*6]
872         add     $np,$np,#8*8
873         //adc   $carry,xzr,xzr          // moved above
874         b       .Lsqr8x_tail
875
876 .align  4
877 .Lsqr8x_tail_break:
878         ldr     $n0,[x29,#112]          // pull n0
879         add     $cnt,$tp,#8*8           // end of current t[num] window
880
881         subs    xzr,$topmost,#1         // "move" top-most carry to carry bit
882         adcs    $t0,$acc0,$a0
883         adcs    $t1,$acc1,$a1
884         ldp     $acc0,$acc1,[$rp,#8*0]
885         adcs    $acc2,$acc2,$a2
886         ldp     $a0,$a1,[$t2,#8*0]      // recall that $t2 is &n[0]
887         adcs    $acc3,$acc3,$a3
888         ldp     $a2,$a3,[$t2,#8*2]
889         adcs    $acc4,$acc4,$a4
890         adcs    $acc5,$acc5,$a5
891         ldp     $a4,$a5,[$t2,#8*4]
892         adcs    $acc6,$acc6,$a6
893         adcs    $acc7,$acc7,$a7
894         ldp     $a6,$a7,[$t2,#8*6]
895         add     $np,$t2,#8*8
896         adc     $topmost,xzr,xzr        // top-most carry
897         mul     $na0,$n0,$acc0
898         stp     $t0,$t1,[$tp,#8*0]
899         stp     $acc2,$acc3,[$tp,#8*2]
900         ldp     $acc2,$acc3,[$rp,#8*2]
901         stp     $acc4,$acc5,[$tp,#8*4]
902         ldp     $acc4,$acc5,[$rp,#8*4]
903         cmp     $cnt,x29                // did we hit the bottom?
904         stp     $acc6,$acc7,[$tp,#8*6]
905         mov     $tp,$rp                 // slide the window
906         ldp     $acc6,$acc7,[$rp,#8*6]
907         mov     $cnt,#8
908         b.ne    .Lsqr8x_reduction
909
910         // Final step. We see if result is larger than modulus, and
911         // if it is, subtract the modulus. But comparison implies
912         // subtraction. So we subtract modulus, see if it borrowed,
913         // and conditionally copy original value.
914         ldr     $rp,[x29,#96]           // pull rp
915         add     $tp,$tp,#8*8
916         subs    $t0,$acc0,$a0
917         sbcs    $t1,$acc1,$a1
918         sub     $cnt,$num,#8*8
919         mov     $ap_end,$rp             // $rp copy
920
921 .Lsqr8x_sub:
922         sbcs    $t2,$acc2,$a2
923         ldp     $a0,$a1,[$np,#8*0]
924         sbcs    $t3,$acc3,$a3
925         stp     $t0,$t1,[$rp,#8*0]
926         sbcs    $t0,$acc4,$a4
927         ldp     $a2,$a3,[$np,#8*2]
928         sbcs    $t1,$acc5,$a5
929         stp     $t2,$t3,[$rp,#8*2]
930         sbcs    $t2,$acc6,$a6
931         ldp     $a4,$a5,[$np,#8*4]
932         sbcs    $t3,$acc7,$a7
933         ldp     $a6,$a7,[$np,#8*6]
934         add     $np,$np,#8*8
935         ldp     $acc0,$acc1,[$tp,#8*0]
936         sub     $cnt,$cnt,#8*8
937         ldp     $acc2,$acc3,[$tp,#8*2]
938         ldp     $acc4,$acc5,[$tp,#8*4]
939         ldp     $acc6,$acc7,[$tp,#8*6]
940         add     $tp,$tp,#8*8
941         stp     $t0,$t1,[$rp,#8*4]
942         sbcs    $t0,$acc0,$a0
943         stp     $t2,$t3,[$rp,#8*6]
944         add     $rp,$rp,#8*8
945         sbcs    $t1,$acc1,$a1
946         cbnz    $cnt,.Lsqr8x_sub
947
948         sbcs    $t2,$acc2,$a2
949          mov    $tp,sp
950          add    $ap,sp,$num
951          ldp    $a0,$a1,[$ap_end,#8*0]
952         sbcs    $t3,$acc3,$a3
953         stp     $t0,$t1,[$rp,#8*0]
954         sbcs    $t0,$acc4,$a4
955          ldp    $a2,$a3,[$ap_end,#8*2]
956         sbcs    $t1,$acc5,$a5
957         stp     $t2,$t3,[$rp,#8*2]
958         sbcs    $t2,$acc6,$a6
959          ldp    $acc0,$acc1,[$ap,#8*0]
960         sbcs    $t3,$acc7,$a7
961          ldp    $acc2,$acc3,[$ap,#8*2]
962         sbcs    xzr,$topmost,xzr        // did it borrow?
963         ldr     x30,[x29,#8]            // pull return address
964         stp     $t0,$t1,[$rp,#8*4]
965         stp     $t2,$t3,[$rp,#8*6]
966
967         sub     $cnt,$num,#8*4
968 .Lsqr4x_cond_copy:
969         sub     $cnt,$cnt,#8*4
970         csel    $t0,$acc0,$a0,lo
971          stp    xzr,xzr,[$tp,#8*0]
972         csel    $t1,$acc1,$a1,lo
973         ldp     $a0,$a1,[$ap_end,#8*4]
974         ldp     $acc0,$acc1,[$ap,#8*4]
975         csel    $t2,$acc2,$a2,lo
976          stp    xzr,xzr,[$tp,#8*2]
977          add    $tp,$tp,#8*4
978         csel    $t3,$acc3,$a3,lo
979         ldp     $a2,$a3,[$ap_end,#8*6]
980         ldp     $acc2,$acc3,[$ap,#8*6]
981         add     $ap,$ap,#8*4
982         stp     $t0,$t1,[$ap_end,#8*0]
983         stp     $t2,$t3,[$ap_end,#8*2]
984         add     $ap_end,$ap_end,#8*4
985          stp    xzr,xzr,[$ap,#8*0]
986          stp    xzr,xzr,[$ap,#8*2]
987         cbnz    $cnt,.Lsqr4x_cond_copy
988
989         csel    $t0,$acc0,$a0,lo
990          stp    xzr,xzr,[$tp,#8*0]
991         csel    $t1,$acc1,$a1,lo
992          stp    xzr,xzr,[$tp,#8*2]
993         csel    $t2,$acc2,$a2,lo
994         csel    $t3,$acc3,$a3,lo
995         stp     $t0,$t1,[$ap_end,#8*0]
996         stp     $t2,$t3,[$ap_end,#8*2]
997
998         b       .Lsqr8x_done
999
1000 .align  4
1001 .Lsqr8x8_post_condition:
1002         adc     $carry,xzr,xzr
1003         ldr     x30,[x29,#8]            // pull return address
1004         // $acc0-7,$carry hold result, $a0-7 hold modulus
1005         subs    $a0,$acc0,$a0
1006         ldr     $ap,[x29,#96]           // pull rp
1007         sbcs    $a1,$acc1,$a1
1008          stp    xzr,xzr,[sp,#8*0]
1009         sbcs    $a2,$acc2,$a2
1010          stp    xzr,xzr,[sp,#8*2]
1011         sbcs    $a3,$acc3,$a3
1012          stp    xzr,xzr,[sp,#8*4]
1013         sbcs    $a4,$acc4,$a4
1014          stp    xzr,xzr,[sp,#8*6]
1015         sbcs    $a5,$acc5,$a5
1016          stp    xzr,xzr,[sp,#8*8]
1017         sbcs    $a6,$acc6,$a6
1018          stp    xzr,xzr,[sp,#8*10]
1019         sbcs    $a7,$acc7,$a7
1020          stp    xzr,xzr,[sp,#8*12]
1021         sbcs    $carry,$carry,xzr       // did it borrow?
1022          stp    xzr,xzr,[sp,#8*14]
1023
1024         // $a0-7 hold result-modulus
1025         csel    $a0,$acc0,$a0,lo
1026         csel    $a1,$acc1,$a1,lo
1027         csel    $a2,$acc2,$a2,lo
1028         csel    $a3,$acc3,$a3,lo
1029         stp     $a0,$a1,[$ap,#8*0]
1030         csel    $a4,$acc4,$a4,lo
1031         csel    $a5,$acc5,$a5,lo
1032         stp     $a2,$a3,[$ap,#8*2]
1033         csel    $a6,$acc6,$a6,lo
1034         csel    $a7,$acc7,$a7,lo
1035         stp     $a4,$a5,[$ap,#8*4]
1036         stp     $a6,$a7,[$ap,#8*6]
1037
1038 .Lsqr8x_done:
1039         ldp     x19,x20,[x29,#16]
1040         mov     sp,x29
1041         ldp     x21,x22,[x29,#32]
1042         mov     x0,#1
1043         ldp     x23,x24,[x29,#48]
1044         ldp     x25,x26,[x29,#64]
1045         ldp     x27,x28,[x29,#80]
1046         ldr     x29,[sp],#128
1047         .inst   0xd50323bf              // autiasp
1048         ret
1049 .size   __bn_sqr8x_mont,.-__bn_sqr8x_mont
1050 ___
1051 }
1052
1053 {
1054 ########################################################################
1055 # Even though this might look as ARMv8 adaptation of mulx4x_mont from
1056 # x86_64-mont5 module, it's different in sense that it performs
1057 # reduction 256 bits at a time.
1058
1059 my ($a0,$a1,$a2,$a3,
1060     $t0,$t1,$t2,$t3,
1061     $m0,$m1,$m2,$m3,
1062     $acc0,$acc1,$acc2,$acc3,$acc4,
1063     $bi,$mi,$tp,$ap_end,$cnt) = map("x$_",(6..17,19..28));
1064 my  $bp_end=$rp;
1065 my  ($carry,$topmost) = ($rp,"x30");
1066
1067 $code.=<<___;
1068 .type   __bn_mul4x_mont,%function
1069 .align  5
1070 __bn_mul4x_mont:
1071         .inst   0xd503233f              // paciasp
1072         stp     x29,x30,[sp,#-128]!
1073         add     x29,sp,#0
1074         stp     x19,x20,[sp,#16]
1075         stp     x21,x22,[sp,#32]
1076         stp     x23,x24,[sp,#48]
1077         stp     x25,x26,[sp,#64]
1078         stp     x27,x28,[sp,#80]
1079
1080         sub     $tp,sp,$num,lsl#3
1081         lsl     $num,$num,#3
1082         ldr     $n0,[$n0]               // *n0
1083         sub     sp,$tp,#8*4             // alloca
1084
1085         add     $t0,$bp,$num
1086         add     $ap_end,$ap,$num
1087         stp     $rp,$t0,[x29,#96]       // offload rp and &b[num]
1088
1089         ldr     $bi,[$bp,#8*0]          // b[0]
1090         ldp     $a0,$a1,[$ap,#8*0]      // a[0..3]
1091         ldp     $a2,$a3,[$ap,#8*2]
1092         add     $ap,$ap,#8*4
1093         mov     $acc0,xzr
1094         mov     $acc1,xzr
1095         mov     $acc2,xzr
1096         mov     $acc3,xzr
1097         ldp     $m0,$m1,[$np,#8*0]      // n[0..3]
1098         ldp     $m2,$m3,[$np,#8*2]
1099         adds    $np,$np,#8*4            // clear carry bit
1100         mov     $carry,xzr
1101         mov     $cnt,#0
1102         mov     $tp,sp
1103
1104 .Loop_mul4x_1st_reduction:
1105         mul     $t0,$a0,$bi             // lo(a[0..3]*b[0])
1106         adc     $carry,$carry,xzr       // modulo-scheduled
1107         mul     $t1,$a1,$bi
1108         add     $cnt,$cnt,#8
1109         mul     $t2,$a2,$bi
1110         and     $cnt,$cnt,#31
1111         mul     $t3,$a3,$bi
1112         adds    $acc0,$acc0,$t0
1113         umulh   $t0,$a0,$bi             // hi(a[0..3]*b[0])
1114         adcs    $acc1,$acc1,$t1
1115         mul     $mi,$acc0,$n0           // t[0]*n0
1116         adcs    $acc2,$acc2,$t2
1117         umulh   $t1,$a1,$bi
1118         adcs    $acc3,$acc3,$t3
1119         umulh   $t2,$a2,$bi
1120         adc     $acc4,xzr,xzr
1121         umulh   $t3,$a3,$bi
1122         ldr     $bi,[$bp,$cnt]          // next b[i] (or b[0])
1123         adds    $acc1,$acc1,$t0
1124         // (*)  mul     $t0,$m0,$mi     // lo(n[0..3]*t[0]*n0)
1125         str     $mi,[$tp],#8            // put aside t[0]*n0 for tail processing
1126         adcs    $acc2,$acc2,$t1
1127         mul     $t1,$m1,$mi
1128         adcs    $acc3,$acc3,$t2
1129         mul     $t2,$m2,$mi
1130         adc     $acc4,$acc4,$t3         // can't overflow
1131         mul     $t3,$m3,$mi
1132         // (*)  adds    xzr,$acc0,$t0
1133         subs    xzr,$acc0,#1            // (*)
1134         umulh   $t0,$m0,$mi             // hi(n[0..3]*t[0]*n0)
1135         adcs    $acc0,$acc1,$t1
1136         umulh   $t1,$m1,$mi
1137         adcs    $acc1,$acc2,$t2
1138         umulh   $t2,$m2,$mi
1139         adcs    $acc2,$acc3,$t3
1140         umulh   $t3,$m3,$mi
1141         adcs    $acc3,$acc4,$carry
1142         adc     $carry,xzr,xzr
1143         adds    $acc0,$acc0,$t0
1144         sub     $t0,$ap_end,$ap
1145         adcs    $acc1,$acc1,$t1
1146         adcs    $acc2,$acc2,$t2
1147         adcs    $acc3,$acc3,$t3
1148         //adc   $carry,$carry,xzr
1149         cbnz    $cnt,.Loop_mul4x_1st_reduction
1150
1151         cbz     $t0,.Lmul4x4_post_condition
1152
1153         ldp     $a0,$a1,[$ap,#8*0]      // a[4..7]
1154         ldp     $a2,$a3,[$ap,#8*2]
1155         add     $ap,$ap,#8*4
1156         ldr     $mi,[sp]                // a[0]*n0
1157         ldp     $m0,$m1,[$np,#8*0]      // n[4..7]
1158         ldp     $m2,$m3,[$np,#8*2]
1159         add     $np,$np,#8*4
1160
1161 .Loop_mul4x_1st_tail:
1162         mul     $t0,$a0,$bi             // lo(a[4..7]*b[i])
1163         adc     $carry,$carry,xzr       // modulo-scheduled
1164         mul     $t1,$a1,$bi
1165         add     $cnt,$cnt,#8
1166         mul     $t2,$a2,$bi
1167         and     $cnt,$cnt,#31
1168         mul     $t3,$a3,$bi
1169         adds    $acc0,$acc0,$t0
1170         umulh   $t0,$a0,$bi             // hi(a[4..7]*b[i])
1171         adcs    $acc1,$acc1,$t1
1172         umulh   $t1,$a1,$bi
1173         adcs    $acc2,$acc2,$t2
1174         umulh   $t2,$a2,$bi
1175         adcs    $acc3,$acc3,$t3
1176         umulh   $t3,$a3,$bi
1177         adc     $acc4,xzr,xzr
1178         ldr     $bi,[$bp,$cnt]          // next b[i] (or b[0])
1179         adds    $acc1,$acc1,$t0
1180         mul     $t0,$m0,$mi             // lo(n[4..7]*a[0]*n0)
1181         adcs    $acc2,$acc2,$t1
1182         mul     $t1,$m1,$mi
1183         adcs    $acc3,$acc3,$t2
1184         mul     $t2,$m2,$mi
1185         adc     $acc4,$acc4,$t3         // can't overflow
1186         mul     $t3,$m3,$mi
1187         adds    $acc0,$acc0,$t0
1188         umulh   $t0,$m0,$mi             // hi(n[4..7]*a[0]*n0)
1189         adcs    $acc1,$acc1,$t1
1190         umulh   $t1,$m1,$mi
1191         adcs    $acc2,$acc2,$t2
1192         umulh   $t2,$m2,$mi
1193         adcs    $acc3,$acc3,$t3
1194         adcs    $acc4,$acc4,$carry
1195         umulh   $t3,$m3,$mi
1196         adc     $carry,xzr,xzr
1197         ldr     $mi,[sp,$cnt]           // next t[0]*n0
1198         str     $acc0,[$tp],#8          // result!!!
1199         adds    $acc0,$acc1,$t0
1200         sub     $t0,$ap_end,$ap         // done yet?
1201         adcs    $acc1,$acc2,$t1
1202         adcs    $acc2,$acc3,$t2
1203         adcs    $acc3,$acc4,$t3
1204         //adc   $carry,$carry,xzr
1205         cbnz    $cnt,.Loop_mul4x_1st_tail
1206
1207         sub     $t1,$ap_end,$num        // rewinded $ap
1208         cbz     $t0,.Lmul4x_proceed
1209
1210         ldp     $a0,$a1,[$ap,#8*0]
1211         ldp     $a2,$a3,[$ap,#8*2]
1212         add     $ap,$ap,#8*4
1213         ldp     $m0,$m1,[$np,#8*0]
1214         ldp     $m2,$m3,[$np,#8*2]
1215         add     $np,$np,#8*4
1216         b       .Loop_mul4x_1st_tail
1217
1218 .align  5
1219 .Lmul4x_proceed:
1220         ldr     $bi,[$bp,#8*4]!         // *++b
1221         adc     $topmost,$carry,xzr
1222         ldp     $a0,$a1,[$t1,#8*0]      // a[0..3]
1223         sub     $np,$np,$num            // rewind np
1224         ldp     $a2,$a3,[$t1,#8*2]
1225         add     $ap,$t1,#8*4
1226
1227         stp     $acc0,$acc1,[$tp,#8*0]  // result!!!
1228         ldp     $acc0,$acc1,[sp,#8*4]   // t[0..3]
1229         stp     $acc2,$acc3,[$tp,#8*2]  // result!!!
1230         ldp     $acc2,$acc3,[sp,#8*6]
1231
1232         ldp     $m0,$m1,[$np,#8*0]      // n[0..3]
1233         mov     $tp,sp
1234         ldp     $m2,$m3,[$np,#8*2]
1235         adds    $np,$np,#8*4            // clear carry bit
1236         mov     $carry,xzr
1237
1238 .align  4
1239 .Loop_mul4x_reduction:
1240         mul     $t0,$a0,$bi             // lo(a[0..3]*b[4])
1241         adc     $carry,$carry,xzr       // modulo-scheduled
1242         mul     $t1,$a1,$bi
1243         add     $cnt,$cnt,#8
1244         mul     $t2,$a2,$bi
1245         and     $cnt,$cnt,#31
1246         mul     $t3,$a3,$bi
1247         adds    $acc0,$acc0,$t0
1248         umulh   $t0,$a0,$bi             // hi(a[0..3]*b[4])
1249         adcs    $acc1,$acc1,$t1
1250         mul     $mi,$acc0,$n0           // t[0]*n0
1251         adcs    $acc2,$acc2,$t2
1252         umulh   $t1,$a1,$bi
1253         adcs    $acc3,$acc3,$t3
1254         umulh   $t2,$a2,$bi
1255         adc     $acc4,xzr,xzr
1256         umulh   $t3,$a3,$bi
1257         ldr     $bi,[$bp,$cnt]          // next b[i]
1258         adds    $acc1,$acc1,$t0
1259         // (*)  mul     $t0,$m0,$mi
1260         str     $mi,[$tp],#8            // put aside t[0]*n0 for tail processing
1261         adcs    $acc2,$acc2,$t1
1262         mul     $t1,$m1,$mi             // lo(n[0..3]*t[0]*n0
1263         adcs    $acc3,$acc3,$t2
1264         mul     $t2,$m2,$mi
1265         adc     $acc4,$acc4,$t3         // can't overflow
1266         mul     $t3,$m3,$mi
1267         // (*)  adds    xzr,$acc0,$t0
1268         subs    xzr,$acc0,#1            // (*)
1269         umulh   $t0,$m0,$mi             // hi(n[0..3]*t[0]*n0
1270         adcs    $acc0,$acc1,$t1
1271         umulh   $t1,$m1,$mi
1272         adcs    $acc1,$acc2,$t2
1273         umulh   $t2,$m2,$mi
1274         adcs    $acc2,$acc3,$t3
1275         umulh   $t3,$m3,$mi
1276         adcs    $acc3,$acc4,$carry
1277         adc     $carry,xzr,xzr
1278         adds    $acc0,$acc0,$t0
1279         adcs    $acc1,$acc1,$t1
1280         adcs    $acc2,$acc2,$t2
1281         adcs    $acc3,$acc3,$t3
1282         //adc   $carry,$carry,xzr
1283         cbnz    $cnt,.Loop_mul4x_reduction
1284
1285         adc     $carry,$carry,xzr
1286         ldp     $t0,$t1,[$tp,#8*4]      // t[4..7]
1287         ldp     $t2,$t3,[$tp,#8*6]
1288         ldp     $a0,$a1,[$ap,#8*0]      // a[4..7]
1289         ldp     $a2,$a3,[$ap,#8*2]
1290         add     $ap,$ap,#8*4
1291         adds    $acc0,$acc0,$t0
1292         adcs    $acc1,$acc1,$t1
1293         adcs    $acc2,$acc2,$t2
1294         adcs    $acc3,$acc3,$t3
1295         //adc   $carry,$carry,xzr
1296
1297         ldr     $mi,[sp]                // t[0]*n0
1298         ldp     $m0,$m1,[$np,#8*0]      // n[4..7]
1299         ldp     $m2,$m3,[$np,#8*2]
1300         add     $np,$np,#8*4
1301
1302 .align  4
1303 .Loop_mul4x_tail:
1304         mul     $t0,$a0,$bi             // lo(a[4..7]*b[4])
1305         adc     $carry,$carry,xzr       // modulo-scheduled
1306         mul     $t1,$a1,$bi
1307         add     $cnt,$cnt,#8
1308         mul     $t2,$a2,$bi
1309         and     $cnt,$cnt,#31
1310         mul     $t3,$a3,$bi
1311         adds    $acc0,$acc0,$t0
1312         umulh   $t0,$a0,$bi             // hi(a[4..7]*b[4])
1313         adcs    $acc1,$acc1,$t1
1314         umulh   $t1,$a1,$bi
1315         adcs    $acc2,$acc2,$t2
1316         umulh   $t2,$a2,$bi
1317         adcs    $acc3,$acc3,$t3
1318         umulh   $t3,$a3,$bi
1319         adc     $acc4,xzr,xzr
1320         ldr     $bi,[$bp,$cnt]          // next b[i]
1321         adds    $acc1,$acc1,$t0
1322         mul     $t0,$m0,$mi             // lo(n[4..7]*t[0]*n0)
1323         adcs    $acc2,$acc2,$t1
1324         mul     $t1,$m1,$mi
1325         adcs    $acc3,$acc3,$t2
1326         mul     $t2,$m2,$mi
1327         adc     $acc4,$acc4,$t3         // can't overflow
1328         mul     $t3,$m3,$mi
1329         adds    $acc0,$acc0,$t0
1330         umulh   $t0,$m0,$mi             // hi(n[4..7]*t[0]*n0)
1331         adcs    $acc1,$acc1,$t1
1332         umulh   $t1,$m1,$mi
1333         adcs    $acc2,$acc2,$t2
1334         umulh   $t2,$m2,$mi
1335         adcs    $acc3,$acc3,$t3
1336         umulh   $t3,$m3,$mi
1337         adcs    $acc4,$acc4,$carry
1338         ldr     $mi,[sp,$cnt]           // next a[0]*n0
1339         adc     $carry,xzr,xzr
1340         str     $acc0,[$tp],#8          // result!!!
1341         adds    $acc0,$acc1,$t0
1342         sub     $t0,$ap_end,$ap         // done yet?
1343         adcs    $acc1,$acc2,$t1
1344         adcs    $acc2,$acc3,$t2
1345         adcs    $acc3,$acc4,$t3
1346         //adc   $carry,$carry,xzr
1347         cbnz    $cnt,.Loop_mul4x_tail
1348
1349         sub     $t1,$np,$num            // rewinded np?
1350         adc     $carry,$carry,xzr
1351         cbz     $t0,.Loop_mul4x_break
1352
1353         ldp     $t0,$t1,[$tp,#8*4]
1354         ldp     $t2,$t3,[$tp,#8*6]
1355         ldp     $a0,$a1,[$ap,#8*0]
1356         ldp     $a2,$a3,[$ap,#8*2]
1357         add     $ap,$ap,#8*4
1358         adds    $acc0,$acc0,$t0
1359         adcs    $acc1,$acc1,$t1
1360         adcs    $acc2,$acc2,$t2
1361         adcs    $acc3,$acc3,$t3
1362         //adc   $carry,$carry,xzr
1363         ldp     $m0,$m1,[$np,#8*0]
1364         ldp     $m2,$m3,[$np,#8*2]
1365         add     $np,$np,#8*4
1366         b       .Loop_mul4x_tail
1367
1368 .align  4
1369 .Loop_mul4x_break:
1370         ldp     $t2,$t3,[x29,#96]       // pull rp and &b[num]
1371         adds    $acc0,$acc0,$topmost
1372         add     $bp,$bp,#8*4            // bp++
1373         adcs    $acc1,$acc1,xzr
1374         sub     $ap,$ap,$num            // rewind ap
1375         adcs    $acc2,$acc2,xzr
1376         stp     $acc0,$acc1,[$tp,#8*0]  // result!!!
1377         adcs    $acc3,$acc3,xzr
1378         ldp     $acc0,$acc1,[sp,#8*4]   // t[0..3]
1379         adc     $topmost,$carry,xzr
1380         stp     $acc2,$acc3,[$tp,#8*2]  // result!!!
1381         cmp     $bp,$t3                 // done yet?
1382         ldp     $acc2,$acc3,[sp,#8*6]
1383         ldp     $m0,$m1,[$t1,#8*0]      // n[0..3]
1384         ldp     $m2,$m3,[$t1,#8*2]
1385         add     $np,$t1,#8*4
1386         b.eq    .Lmul4x_post
1387
1388         ldr     $bi,[$bp]
1389         ldp     $a0,$a1,[$ap,#8*0]      // a[0..3]
1390         ldp     $a2,$a3,[$ap,#8*2]
1391         adds    $ap,$ap,#8*4            // clear carry bit
1392         mov     $carry,xzr
1393         mov     $tp,sp
1394         b       .Loop_mul4x_reduction
1395
1396 .align  4
1397 .Lmul4x_post:
1398         // Final step. We see if result is larger than modulus, and
1399         // if it is, subtract the modulus. But comparison implies
1400         // subtraction. So we subtract modulus, see if it borrowed,
1401         // and conditionally copy original value.
1402         mov     $rp,$t2
1403         mov     $ap_end,$t2             // $rp copy
1404         subs    $t0,$acc0,$m0
1405         add     $tp,sp,#8*8
1406         sbcs    $t1,$acc1,$m1
1407         sub     $cnt,$num,#8*4
1408
1409 .Lmul4x_sub:
1410         sbcs    $t2,$acc2,$m2
1411         ldp     $m0,$m1,[$np,#8*0]
1412         sub     $cnt,$cnt,#8*4
1413         ldp     $acc0,$acc1,[$tp,#8*0]
1414         sbcs    $t3,$acc3,$m3
1415         ldp     $m2,$m3,[$np,#8*2]
1416         add     $np,$np,#8*4
1417         ldp     $acc2,$acc3,[$tp,#8*2]
1418         add     $tp,$tp,#8*4
1419         stp     $t0,$t1,[$rp,#8*0]
1420         sbcs    $t0,$acc0,$m0
1421         stp     $t2,$t3,[$rp,#8*2]
1422         add     $rp,$rp,#8*4
1423         sbcs    $t1,$acc1,$m1
1424         cbnz    $cnt,.Lmul4x_sub
1425
1426         sbcs    $t2,$acc2,$m2
1427          mov    $tp,sp
1428          add    $ap,sp,#8*4
1429          ldp    $a0,$a1,[$ap_end,#8*0]
1430         sbcs    $t3,$acc3,$m3
1431         stp     $t0,$t1,[$rp,#8*0]
1432          ldp    $a2,$a3,[$ap_end,#8*2]
1433         stp     $t2,$t3,[$rp,#8*2]
1434          ldp    $acc0,$acc1,[$ap,#8*0]
1435          ldp    $acc2,$acc3,[$ap,#8*2]
1436         sbcs    xzr,$topmost,xzr        // did it borrow?
1437         ldr     x30,[x29,#8]            // pull return address
1438
1439         sub     $cnt,$num,#8*4
1440 .Lmul4x_cond_copy:
1441         sub     $cnt,$cnt,#8*4
1442         csel    $t0,$acc0,$a0,lo
1443          stp    xzr,xzr,[$tp,#8*0]
1444         csel    $t1,$acc1,$a1,lo
1445         ldp     $a0,$a1,[$ap_end,#8*4]
1446         ldp     $acc0,$acc1,[$ap,#8*4]
1447         csel    $t2,$acc2,$a2,lo
1448          stp    xzr,xzr,[$tp,#8*2]
1449          add    $tp,$tp,#8*4
1450         csel    $t3,$acc3,$a3,lo
1451         ldp     $a2,$a3,[$ap_end,#8*6]
1452         ldp     $acc2,$acc3,[$ap,#8*6]
1453         add     $ap,$ap,#8*4
1454         stp     $t0,$t1,[$ap_end,#8*0]
1455         stp     $t2,$t3,[$ap_end,#8*2]
1456         add     $ap_end,$ap_end,#8*4
1457         cbnz    $cnt,.Lmul4x_cond_copy
1458
1459         csel    $t0,$acc0,$a0,lo
1460          stp    xzr,xzr,[$tp,#8*0]
1461         csel    $t1,$acc1,$a1,lo
1462          stp    xzr,xzr,[$tp,#8*2]
1463         csel    $t2,$acc2,$a2,lo
1464          stp    xzr,xzr,[$tp,#8*3]
1465         csel    $t3,$acc3,$a3,lo
1466          stp    xzr,xzr,[$tp,#8*4]
1467         stp     $t0,$t1,[$ap_end,#8*0]
1468         stp     $t2,$t3,[$ap_end,#8*2]
1469
1470         b       .Lmul4x_done
1471
1472 .align  4
1473 .Lmul4x4_post_condition:
1474         adc     $carry,$carry,xzr
1475         ldr     $ap,[x29,#96]           // pull rp
1476         // $acc0-3,$carry hold result, $m0-7 hold modulus
1477         subs    $a0,$acc0,$m0
1478         ldr     x30,[x29,#8]            // pull return address
1479         sbcs    $a1,$acc1,$m1
1480          stp    xzr,xzr,[sp,#8*0]
1481         sbcs    $a2,$acc2,$m2
1482          stp    xzr,xzr,[sp,#8*2]
1483         sbcs    $a3,$acc3,$m3
1484          stp    xzr,xzr,[sp,#8*4]
1485         sbcs    xzr,$carry,xzr          // did it borrow?
1486          stp    xzr,xzr,[sp,#8*6]
1487
1488         // $a0-3 hold result-modulus
1489         csel    $a0,$acc0,$a0,lo
1490         csel    $a1,$acc1,$a1,lo
1491         csel    $a2,$acc2,$a2,lo
1492         csel    $a3,$acc3,$a3,lo
1493         stp     $a0,$a1,[$ap,#8*0]
1494         stp     $a2,$a3,[$ap,#8*2]
1495
1496 .Lmul4x_done:
1497         ldp     x19,x20,[x29,#16]
1498         mov     sp,x29
1499         ldp     x21,x22,[x29,#32]
1500         mov     x0,#1
1501         ldp     x23,x24,[x29,#48]
1502         ldp     x25,x26,[x29,#64]
1503         ldp     x27,x28,[x29,#80]
1504         ldr     x29,[sp],#128
1505         .inst   0xd50323bf              // autiasp
1506         ret
1507 .size   __bn_mul4x_mont,.-__bn_mul4x_mont
1508 ___
1509 }
1510 $code.=<<___;
1511 .asciz  "Montgomery Multiplication for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
1512 .align  4
1513 ___
1514
1515 print $code;
1516
1517 close STDOUT or die "error closing STDOUT";