ec/asm/ecp_nistz256-*.pl: addition to perform stricter reduction.
[openssl.git] / crypto / ec / asm / ecp_nistz256-armv8.pl
1 #! /usr/bin/env perl
2 # Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved.
3 #
4 # Licensed under the OpenSSL license (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 # ECP_NISTZ256 module for ARMv8.
18 #
19 # February 2015.
20 #
21 # Original ECP_NISTZ256 submission targeting x86_64 is detailed in
22 # http://eprint.iacr.org/2013/816.
23 #
24 #                       with/without -DECP_NISTZ256_ASM
25 # Apple A7              +120-360%
26 # Cortex-A53            +120-400%
27 # Cortex-A57            +120-350%
28 # X-Gene                +200-330%
29 # Denver                +140-400%
30 #
31 # Ranges denote minimum and maximum improvement coefficients depending
32 # on benchmark. Lower coefficients are for ECDSA sign, server-side
33 # operation. Keep in mind that +400% means 5x improvement.
34
35 $flavour = shift;
36 while (($output=shift) && ($output!~/\w[\w\-]*\.\w+$/)) {}
37
38 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
39 ( $xlate="${dir}arm-xlate.pl" and -f $xlate ) or
40 ( $xlate="${dir}../../perlasm/arm-xlate.pl" and -f $xlate) or
41 die "can't locate arm-xlate.pl";
42
43 open OUT,"| \"$^X\" $xlate $flavour $output";
44 *STDOUT=*OUT;
45
46 {
47 my ($rp,$ap,$bp,$bi,$a0,$a1,$a2,$a3,$t0,$t1,$t2,$t3,$poly1,$poly3,
48     $acc0,$acc1,$acc2,$acc3,$acc4,$acc5) =
49     map("x$_",(0..17,19,20));
50
51 my ($acc6,$acc7)=($ap,$bp);     # used in __ecp_nistz256_sqr_mont
52
53 $code.=<<___;
54 #include "arm_arch.h"
55
56 .text
57 ___
58 ########################################################################
59 # Convert ecp_nistz256_table.c to layout expected by ecp_nistz_gather_w7
60 #
61 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
62 open TABLE,"<ecp_nistz256_table.c"              or
63 open TABLE,"<${dir}../ecp_nistz256_table.c"     or
64 die "failed to open ecp_nistz256_table.c:",$!;
65
66 use integer;
67
68 foreach(<TABLE>) {
69         s/TOBN\(\s*(0x[0-9a-f]+),\s*(0x[0-9a-f]+)\s*\)/push @arr,hex($2),hex($1)/geo;
70 }
71 close TABLE;
72
73 # See ecp_nistz256_table.c for explanation for why it's 64*16*37.
74 # 64*16*37-1 is because $#arr returns last valid index or @arr, not
75 # amount of elements.
76 die "insane number of elements" if ($#arr != 64*16*37-1);
77
78 $code.=<<___;
79 .globl  ecp_nistz256_precomputed
80 .type   ecp_nistz256_precomputed,%object
81 .align  12
82 ecp_nistz256_precomputed:
83 ___
84 ########################################################################
85 # this conversion smashes P256_POINT_AFFINE by individual bytes with
86 # 64 byte interval, similar to
87 #       1111222233334444
88 #       1234123412341234
89 for(1..37) {
90         @tbl = splice(@arr,0,64*16);
91         for($i=0;$i<64;$i++) {
92                 undef @line;
93                 for($j=0;$j<64;$j++) {
94                         push @line,(@tbl[$j*16+$i/4]>>(($i%4)*8))&0xff;
95                 }
96                 $code.=".byte\t";
97                 $code.=join(',',map { sprintf "0x%02x",$_} @line);
98                 $code.="\n";
99         }
100 }
101 $code.=<<___;
102 .size   ecp_nistz256_precomputed,.-ecp_nistz256_precomputed
103 .align  5
104 .Lpoly:
105 .quad   0xffffffffffffffff,0x00000000ffffffff,0x0000000000000000,0xffffffff00000001
106 .LRR:   // 2^512 mod P precomputed for NIST P256 polynomial
107 .quad   0x0000000000000003,0xfffffffbffffffff,0xfffffffffffffffe,0x00000004fffffffd
108 .Lone_mont:
109 .quad   0x0000000000000001,0xffffffff00000000,0xffffffffffffffff,0x00000000fffffffe
110 .Lone:
111 .quad   1,0,0,0
112 .asciz  "ECP_NISTZ256 for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
113
114 // void ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
115 .globl  ecp_nistz256_to_mont
116 .type   ecp_nistz256_to_mont,%function
117 .align  6
118 ecp_nistz256_to_mont:
119         stp     x29,x30,[sp,#-32]!
120         add     x29,sp,#0
121         stp     x19,x20,[sp,#16]
122
123         ldr     $bi,.LRR                // bp[0]
124         ldp     $a0,$a1,[$ap]
125         ldp     $a2,$a3,[$ap,#16]
126         ldr     $poly1,.Lpoly+8
127         ldr     $poly3,.Lpoly+24
128         adr     $bp,.LRR                // &bp[0]
129
130         bl      __ecp_nistz256_mul_mont
131
132         ldp     x19,x20,[sp,#16]
133         ldp     x29,x30,[sp],#32
134         ret
135 .size   ecp_nistz256_to_mont,.-ecp_nistz256_to_mont
136
137 // void ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
138 .globl  ecp_nistz256_from_mont
139 .type   ecp_nistz256_from_mont,%function
140 .align  4
141 ecp_nistz256_from_mont:
142         stp     x29,x30,[sp,#-32]!
143         add     x29,sp,#0
144         stp     x19,x20,[sp,#16]
145
146         mov     $bi,#1                  // bp[0]
147         ldp     $a0,$a1,[$ap]
148         ldp     $a2,$a3,[$ap,#16]
149         ldr     $poly1,.Lpoly+8
150         ldr     $poly3,.Lpoly+24
151         adr     $bp,.Lone               // &bp[0]
152
153         bl      __ecp_nistz256_mul_mont
154
155         ldp     x19,x20,[sp,#16]
156         ldp     x29,x30,[sp],#32
157         ret
158 .size   ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
159
160 // void ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4],
161 //                                           const BN_ULONG x2[4]);
162 .globl  ecp_nistz256_mul_mont
163 .type   ecp_nistz256_mul_mont,%function
164 .align  4
165 ecp_nistz256_mul_mont:
166         stp     x29,x30,[sp,#-32]!
167         add     x29,sp,#0
168         stp     x19,x20,[sp,#16]
169
170         ldr     $bi,[$bp]               // bp[0]
171         ldp     $a0,$a1,[$ap]
172         ldp     $a2,$a3,[$ap,#16]
173         ldr     $poly1,.Lpoly+8
174         ldr     $poly3,.Lpoly+24
175
176         bl      __ecp_nistz256_mul_mont
177
178         ldp     x19,x20,[sp,#16]
179         ldp     x29,x30,[sp],#32
180         ret
181 .size   ecp_nistz256_mul_mont,.-ecp_nistz256_mul_mont
182
183 // void ecp_nistz256_sqr_mont(BN_ULONG x0[4],const BN_ULONG x1[4]);
184 .globl  ecp_nistz256_sqr_mont
185 .type   ecp_nistz256_sqr_mont,%function
186 .align  4
187 ecp_nistz256_sqr_mont:
188         stp     x29,x30,[sp,#-32]!
189         add     x29,sp,#0
190         stp     x19,x20,[sp,#16]
191
192         ldp     $a0,$a1,[$ap]
193         ldp     $a2,$a3,[$ap,#16]
194         ldr     $poly1,.Lpoly+8
195         ldr     $poly3,.Lpoly+24
196
197         bl      __ecp_nistz256_sqr_mont
198
199         ldp     x19,x20,[sp,#16]
200         ldp     x29,x30,[sp],#32
201         ret
202 .size   ecp_nistz256_sqr_mont,.-ecp_nistz256_sqr_mont
203
204 // void ecp_nistz256_add(BN_ULONG x0[4],const BN_ULONG x1[4],
205 //                                      const BN_ULONG x2[4]);
206 .globl  ecp_nistz256_add
207 .type   ecp_nistz256_add,%function
208 .align  4
209 ecp_nistz256_add:
210         stp     x29,x30,[sp,#-16]!
211         add     x29,sp,#0
212
213         ldp     $acc0,$acc1,[$ap]
214         ldp     $t0,$t1,[$bp]
215         ldp     $acc2,$acc3,[$ap,#16]
216         ldp     $t2,$t3,[$bp,#16]
217         ldr     $poly1,.Lpoly+8
218         ldr     $poly3,.Lpoly+24
219
220         bl      __ecp_nistz256_add
221
222         ldp     x29,x30,[sp],#16
223         ret
224 .size   ecp_nistz256_add,.-ecp_nistz256_add
225
226 // void ecp_nistz256_div_by_2(BN_ULONG x0[4],const BN_ULONG x1[4]);
227 .globl  ecp_nistz256_div_by_2
228 .type   ecp_nistz256_div_by_2,%function
229 .align  4
230 ecp_nistz256_div_by_2:
231         stp     x29,x30,[sp,#-16]!
232         add     x29,sp,#0
233
234         ldp     $acc0,$acc1,[$ap]
235         ldp     $acc2,$acc3,[$ap,#16]
236         ldr     $poly1,.Lpoly+8
237         ldr     $poly3,.Lpoly+24
238
239         bl      __ecp_nistz256_div_by_2
240
241         ldp     x29,x30,[sp],#16
242         ret
243 .size   ecp_nistz256_div_by_2,.-ecp_nistz256_div_by_2
244
245 // void ecp_nistz256_mul_by_2(BN_ULONG x0[4],const BN_ULONG x1[4]);
246 .globl  ecp_nistz256_mul_by_2
247 .type   ecp_nistz256_mul_by_2,%function
248 .align  4
249 ecp_nistz256_mul_by_2:
250         stp     x29,x30,[sp,#-16]!
251         add     x29,sp,#0
252
253         ldp     $acc0,$acc1,[$ap]
254         ldp     $acc2,$acc3,[$ap,#16]
255         ldr     $poly1,.Lpoly+8
256         ldr     $poly3,.Lpoly+24
257         mov     $t0,$acc0
258         mov     $t1,$acc1
259         mov     $t2,$acc2
260         mov     $t3,$acc3
261
262         bl      __ecp_nistz256_add      // ret = a+a    // 2*a
263
264         ldp     x29,x30,[sp],#16
265         ret
266 .size   ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
267
268 // void ecp_nistz256_mul_by_3(BN_ULONG x0[4],const BN_ULONG x1[4]);
269 .globl  ecp_nistz256_mul_by_3
270 .type   ecp_nistz256_mul_by_3,%function
271 .align  4
272 ecp_nistz256_mul_by_3:
273         stp     x29,x30,[sp,#-16]!
274         add     x29,sp,#0
275
276         ldp     $acc0,$acc1,[$ap]
277         ldp     $acc2,$acc3,[$ap,#16]
278         ldr     $poly1,.Lpoly+8
279         ldr     $poly3,.Lpoly+24
280         mov     $t0,$acc0
281         mov     $t1,$acc1
282         mov     $t2,$acc2
283         mov     $t3,$acc3
284         mov     $a0,$acc0
285         mov     $a1,$acc1
286         mov     $a2,$acc2
287         mov     $a3,$acc3
288
289         bl      __ecp_nistz256_add      // ret = a+a    // 2*a
290
291         mov     $t0,$a0
292         mov     $t1,$a1
293         mov     $t2,$a2
294         mov     $t3,$a3
295
296         bl      __ecp_nistz256_add      // ret += a     // 2*a+a=3*a
297
298         ldp     x29,x30,[sp],#16
299         ret
300 .size   ecp_nistz256_mul_by_3,.-ecp_nistz256_mul_by_3
301
302 // void ecp_nistz256_sub(BN_ULONG x0[4],const BN_ULONG x1[4],
303 //                                      const BN_ULONG x2[4]);
304 .globl  ecp_nistz256_sub
305 .type   ecp_nistz256_sub,%function
306 .align  4
307 ecp_nistz256_sub:
308         stp     x29,x30,[sp,#-16]!
309         add     x29,sp,#0
310
311         ldp     $acc0,$acc1,[$ap]
312         ldp     $acc2,$acc3,[$ap,#16]
313         ldr     $poly1,.Lpoly+8
314         ldr     $poly3,.Lpoly+24
315
316         bl      __ecp_nistz256_sub_from
317
318         ldp     x29,x30,[sp],#16
319         ret
320 .size   ecp_nistz256_sub,.-ecp_nistz256_sub
321
322 // void ecp_nistz256_neg(BN_ULONG x0[4],const BN_ULONG x1[4]);
323 .globl  ecp_nistz256_neg
324 .type   ecp_nistz256_neg,%function
325 .align  4
326 ecp_nistz256_neg:
327         stp     x29,x30,[sp,#-16]!
328         add     x29,sp,#0
329
330         mov     $bp,$ap
331         mov     $acc0,xzr               // a = 0
332         mov     $acc1,xzr
333         mov     $acc2,xzr
334         mov     $acc3,xzr
335         ldr     $poly1,.Lpoly+8
336         ldr     $poly3,.Lpoly+24
337
338         bl      __ecp_nistz256_sub_from
339
340         ldp     x29,x30,[sp],#16
341         ret
342 .size   ecp_nistz256_neg,.-ecp_nistz256_neg
343
344 // note that __ecp_nistz256_mul_mont expects a[0-3] input pre-loaded
345 // to $a0-$a3 and b[0] - to $bi
346 .type   __ecp_nistz256_mul_mont,%function
347 .align  4
348 __ecp_nistz256_mul_mont:
349         mul     $acc0,$a0,$bi           // a[0]*b[0]
350         umulh   $t0,$a0,$bi
351
352         mul     $acc1,$a1,$bi           // a[1]*b[0]
353         umulh   $t1,$a1,$bi
354
355         mul     $acc2,$a2,$bi           // a[2]*b[0]
356         umulh   $t2,$a2,$bi
357
358         mul     $acc3,$a3,$bi           // a[3]*b[0]
359         umulh   $t3,$a3,$bi
360         ldr     $bi,[$bp,#8]            // b[1]
361
362         adds    $acc1,$acc1,$t0         // accumulate high parts of multiplication
363          lsl    $t0,$acc0,#32
364         adcs    $acc2,$acc2,$t1
365          lsr    $t1,$acc0,#32
366         adcs    $acc3,$acc3,$t2
367         adc     $acc4,xzr,$t3
368         mov     $acc5,xzr
369 ___
370 for($i=1;$i<4;$i++) {
371         # Reduction iteration is normally performed by accumulating
372         # result of multiplication of modulus by "magic" digit [and
373         # omitting least significant word, which is guaranteed to
374         # be 0], but thanks to special form of modulus and "magic"
375         # digit being equal to least significant word, it can be
376         # performed with additions and subtractions alone. Indeed:
377         #
378         #            ffff0001.00000000.0000ffff.ffffffff
379         # *                                     abcdefgh
380         # + xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
381         #
382         # Now observing that ff..ff*x = (2^n-1)*x = 2^n*x-x, we
383         # rewrite above as:
384         #
385         #   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.abcdefgh
386         # + abcdefgh.abcdefgh.0000abcd.efgh0000.00000000
387         # - 0000abcd.efgh0000.00000000.00000000.abcdefgh
388         #
389         # or marking redundant operations:
390         #
391         #   xxxxxxxx.xxxxxxxx.xxxxxxxx.xxxxxxxx.--------
392         # + abcdefgh.abcdefgh.0000abcd.efgh0000.--------
393         # - 0000abcd.efgh0000.--------.--------.--------
394
395 $code.=<<___;
396         subs    $t2,$acc0,$t0           // "*0xffff0001"
397         sbc     $t3,$acc0,$t1
398         adds    $acc0,$acc1,$t0         // +=acc[0]<<96 and omit acc[0]
399          mul    $t0,$a0,$bi             // lo(a[0]*b[i])
400         adcs    $acc1,$acc2,$t1
401          mul    $t1,$a1,$bi             // lo(a[1]*b[i])
402         adcs    $acc2,$acc3,$t2         // +=acc[0]*0xffff0001
403          mul    $t2,$a2,$bi             // lo(a[2]*b[i])
404         adcs    $acc3,$acc4,$t3
405          mul    $t3,$a3,$bi             // lo(a[3]*b[i])
406         adc     $acc4,$acc5,xzr
407
408         adds    $acc0,$acc0,$t0         // accumulate low parts of multiplication
409          umulh  $t0,$a0,$bi             // hi(a[0]*b[i])
410         adcs    $acc1,$acc1,$t1
411          umulh  $t1,$a1,$bi             // hi(a[1]*b[i])
412         adcs    $acc2,$acc2,$t2
413          umulh  $t2,$a2,$bi             // hi(a[2]*b[i])
414         adcs    $acc3,$acc3,$t3
415          umulh  $t3,$a3,$bi             // hi(a[3]*b[i])
416         adc     $acc4,$acc4,xzr
417 ___
418 $code.=<<___    if ($i<3);
419         ldr     $bi,[$bp,#8*($i+1)]     // b[$i+1]
420 ___
421 $code.=<<___;
422         adds    $acc1,$acc1,$t0         // accumulate high parts of multiplication
423          lsl    $t0,$acc0,#32
424         adcs    $acc2,$acc2,$t1
425          lsr    $t1,$acc0,#32
426         adcs    $acc3,$acc3,$t2
427         adcs    $acc4,$acc4,$t3
428         adc     $acc5,xzr,xzr
429 ___
430 }
431 $code.=<<___;
432         // last reduction
433         subs    $t2,$acc0,$t0           // "*0xffff0001"
434         sbc     $t3,$acc0,$t1
435         adds    $acc0,$acc1,$t0         // +=acc[0]<<96 and omit acc[0]
436         adcs    $acc1,$acc2,$t1
437         adcs    $acc2,$acc3,$t2         // +=acc[0]*0xffff0001
438         adcs    $acc3,$acc4,$t3
439         adc     $acc4,$acc5,xzr
440
441         adds    $t0,$acc0,#1            // subs $t0,$acc0,#-1 // tmp = ret-modulus
442         sbcs    $t1,$acc1,$poly1
443         sbcs    $t2,$acc2,xzr
444         sbcs    $t3,$acc3,$poly3
445         sbcs    xzr,$acc4,xzr           // did it borrow?
446
447         csel    $acc0,$acc0,$t0,lo      // ret = borrow ? ret : ret-modulus
448         csel    $acc1,$acc1,$t1,lo
449         csel    $acc2,$acc2,$t2,lo
450         stp     $acc0,$acc1,[$rp]
451         csel    $acc3,$acc3,$t3,lo
452         stp     $acc2,$acc3,[$rp,#16]
453
454         ret
455 .size   __ecp_nistz256_mul_mont,.-__ecp_nistz256_mul_mont
456
457 // note that __ecp_nistz256_sqr_mont expects a[0-3] input pre-loaded
458 // to $a0-$a3
459 .type   __ecp_nistz256_sqr_mont,%function
460 .align  4
461 __ecp_nistz256_sqr_mont:
462         //  |  |  |  |  |  |a1*a0|  |
463         //  |  |  |  |  |a2*a0|  |  |
464         //  |  |a3*a2|a3*a0|  |  |  |
465         //  |  |  |  |a2*a1|  |  |  |
466         //  |  |  |a3*a1|  |  |  |  |
467         // *|  |  |  |  |  |  |  | 2|
468         // +|a3*a3|a2*a2|a1*a1|a0*a0|
469         //  |--+--+--+--+--+--+--+--|
470         //  |A7|A6|A5|A4|A3|A2|A1|A0|, where Ax is $accx, i.e. follow $accx
471         //
472         //  "can't overflow" below mark carrying into high part of
473         //  multiplication result, which can't overflow, because it
474         //  can never be all ones.
475
476         mul     $acc1,$a1,$a0           // a[1]*a[0]
477         umulh   $t1,$a1,$a0
478         mul     $acc2,$a2,$a0           // a[2]*a[0]
479         umulh   $t2,$a2,$a0
480         mul     $acc3,$a3,$a0           // a[3]*a[0]
481         umulh   $acc4,$a3,$a0
482
483         adds    $acc2,$acc2,$t1         // accumulate high parts of multiplication
484          mul    $t0,$a2,$a1             // a[2]*a[1]
485          umulh  $t1,$a2,$a1
486         adcs    $acc3,$acc3,$t2
487          mul    $t2,$a3,$a1             // a[3]*a[1]
488          umulh  $t3,$a3,$a1
489         adc     $acc4,$acc4,xzr         // can't overflow
490
491         mul     $acc5,$a3,$a2           // a[3]*a[2]
492         umulh   $acc6,$a3,$a2
493
494         adds    $t1,$t1,$t2             // accumulate high parts of multiplication
495          mul    $acc0,$a0,$a0           // a[0]*a[0]
496         adc     $t2,$t3,xzr             // can't overflow
497
498         adds    $acc3,$acc3,$t0         // accumulate low parts of multiplication
499          umulh  $a0,$a0,$a0
500         adcs    $acc4,$acc4,$t1
501          mul    $t1,$a1,$a1             // a[1]*a[1]
502         adcs    $acc5,$acc5,$t2
503          umulh  $a1,$a1,$a1
504         adc     $acc6,$acc6,xzr         // can't overflow
505
506         adds    $acc1,$acc1,$acc1       // acc[1-6]*=2
507          mul    $t2,$a2,$a2             // a[2]*a[2]
508         adcs    $acc2,$acc2,$acc2
509          umulh  $a2,$a2,$a2
510         adcs    $acc3,$acc3,$acc3
511          mul    $t3,$a3,$a3             // a[3]*a[3]
512         adcs    $acc4,$acc4,$acc4
513          umulh  $a3,$a3,$a3
514         adcs    $acc5,$acc5,$acc5
515         adcs    $acc6,$acc6,$acc6
516         adc     $acc7,xzr,xzr
517
518         adds    $acc1,$acc1,$a0         // +a[i]*a[i]
519         adcs    $acc2,$acc2,$t1
520         adcs    $acc3,$acc3,$a1
521         adcs    $acc4,$acc4,$t2
522         adcs    $acc5,$acc5,$a2
523          lsl    $t0,$acc0,#32
524         adcs    $acc6,$acc6,$t3
525          lsr    $t1,$acc0,#32
526         adc     $acc7,$acc7,$a3
527 ___
528 for($i=0;$i<3;$i++) {                   # reductions, see commentary in
529                                         # multiplication for details
530 $code.=<<___;
531         subs    $t2,$acc0,$t0           // "*0xffff0001"
532         sbc     $t3,$acc0,$t1
533         adds    $acc0,$acc1,$t0         // +=acc[0]<<96 and omit acc[0]
534         adcs    $acc1,$acc2,$t1
535          lsl    $t0,$acc0,#32
536         adcs    $acc2,$acc3,$t2         // +=acc[0]*0xffff0001
537          lsr    $t1,$acc0,#32
538         adc     $acc3,$t3,xzr           // can't overflow
539 ___
540 }
541 $code.=<<___;
542         subs    $t2,$acc0,$t0           // "*0xffff0001"
543         sbc     $t3,$acc0,$t1
544         adds    $acc0,$acc1,$t0         // +=acc[0]<<96 and omit acc[0]
545         adcs    $acc1,$acc2,$t1
546         adcs    $acc2,$acc3,$t2         // +=acc[0]*0xffff0001
547         adc     $acc3,$t3,xzr           // can't overflow
548
549         adds    $acc0,$acc0,$acc4       // accumulate upper half
550         adcs    $acc1,$acc1,$acc5
551         adcs    $acc2,$acc2,$acc6
552         adcs    $acc3,$acc3,$acc7
553         adc     $acc4,xzr,xzr
554
555         adds    $t0,$acc0,#1            // subs $t0,$acc0,#-1 // tmp = ret-modulus
556         sbcs    $t1,$acc1,$poly1
557         sbcs    $t2,$acc2,xzr
558         sbcs    $t3,$acc3,$poly3
559         sbcs    xzr,$acc4,xzr           // did it borrow?
560
561         csel    $acc0,$acc0,$t0,lo      // ret = borrow ? ret : ret-modulus
562         csel    $acc1,$acc1,$t1,lo
563         csel    $acc2,$acc2,$t2,lo
564         stp     $acc0,$acc1,[$rp]
565         csel    $acc3,$acc3,$t3,lo
566         stp     $acc2,$acc3,[$rp,#16]
567
568         ret
569 .size   __ecp_nistz256_sqr_mont,.-__ecp_nistz256_sqr_mont
570
571 // Note that __ecp_nistz256_add expects both input vectors pre-loaded to
572 // $a0-$a3 and $t0-$t3. This is done because it's used in multiple
573 // contexts, e.g. in multiplication by 2 and 3...
574 .type   __ecp_nistz256_add,%function
575 .align  4
576 __ecp_nistz256_add:
577         adds    $acc0,$acc0,$t0         // ret = a+b
578         adcs    $acc1,$acc1,$t1
579         adcs    $acc2,$acc2,$t2
580         adcs    $acc3,$acc3,$t3
581         adc     $ap,xzr,xzr             // zap $ap
582
583         adds    $t0,$acc0,#1            // subs $t0,$a0,#-1 // tmp = ret-modulus
584         sbcs    $t1,$acc1,$poly1
585         sbcs    $t2,$acc2,xzr
586         sbcs    $t3,$acc3,$poly3
587         sbcs    xzr,$ap,xzr             // did subtraction borrow?
588
589         csel    $acc0,$acc0,$t0,lo      // ret = borrow ? ret : ret-modulus
590         csel    $acc1,$acc1,$t1,lo
591         csel    $acc2,$acc2,$t2,lo
592         stp     $acc0,$acc1,[$rp]
593         csel    $acc3,$acc3,$t3,lo
594         stp     $acc2,$acc3,[$rp,#16]
595
596         ret
597 .size   __ecp_nistz256_add,.-__ecp_nistz256_add
598
599 .type   __ecp_nistz256_sub_from,%function
600 .align  4
601 __ecp_nistz256_sub_from:
602         ldp     $t0,$t1,[$bp]
603         ldp     $t2,$t3,[$bp,#16]
604         subs    $acc0,$acc0,$t0         // ret = a-b
605         sbcs    $acc1,$acc1,$t1
606         sbcs    $acc2,$acc2,$t2
607         sbcs    $acc3,$acc3,$t3
608         sbc     $ap,xzr,xzr             // zap $ap
609
610         subs    $t0,$acc0,#1            // adds $t0,$a0,#-1 // tmp = ret+modulus
611         adcs    $t1,$acc1,$poly1
612         adcs    $t2,$acc2,xzr
613         adc     $t3,$acc3,$poly3
614         cmp     $ap,xzr                 // did subtraction borrow?
615
616         csel    $acc0,$acc0,$t0,eq      // ret = borrow ? ret+modulus : ret
617         csel    $acc1,$acc1,$t1,eq
618         csel    $acc2,$acc2,$t2,eq
619         stp     $acc0,$acc1,[$rp]
620         csel    $acc3,$acc3,$t3,eq
621         stp     $acc2,$acc3,[$rp,#16]
622
623         ret
624 .size   __ecp_nistz256_sub_from,.-__ecp_nistz256_sub_from
625
626 .type   __ecp_nistz256_sub_morf,%function
627 .align  4
628 __ecp_nistz256_sub_morf:
629         ldp     $t0,$t1,[$bp]
630         ldp     $t2,$t3,[$bp,#16]
631         subs    $acc0,$t0,$acc0         // ret = b-a
632         sbcs    $acc1,$t1,$acc1
633         sbcs    $acc2,$t2,$acc2
634         sbcs    $acc3,$t3,$acc3
635         sbc     $ap,xzr,xzr             // zap $ap
636
637         subs    $t0,$acc0,#1            // adds $t0,$a0,#-1 // tmp = ret+modulus
638         adcs    $t1,$acc1,$poly1
639         adcs    $t2,$acc2,xzr
640         adc     $t3,$acc3,$poly3
641         cmp     $ap,xzr                 // did subtraction borrow?
642
643         csel    $acc0,$acc0,$t0,eq      // ret = borrow ? ret+modulus : ret
644         csel    $acc1,$acc1,$t1,eq
645         csel    $acc2,$acc2,$t2,eq
646         stp     $acc0,$acc1,[$rp]
647         csel    $acc3,$acc3,$t3,eq
648         stp     $acc2,$acc3,[$rp,#16]
649
650         ret
651 .size   __ecp_nistz256_sub_morf,.-__ecp_nistz256_sub_morf
652
653 .type   __ecp_nistz256_div_by_2,%function
654 .align  4
655 __ecp_nistz256_div_by_2:
656         subs    $t0,$acc0,#1            // adds $t0,$a0,#-1 // tmp = a+modulus
657         adcs    $t1,$acc1,$poly1
658         adcs    $t2,$acc2,xzr
659         adcs    $t3,$acc3,$poly3
660         adc     $ap,xzr,xzr             // zap $ap
661         tst     $acc0,#1                // is a even?
662
663         csel    $acc0,$acc0,$t0,eq      // ret = even ? a : a+modulus 
664         csel    $acc1,$acc1,$t1,eq
665         csel    $acc2,$acc2,$t2,eq
666         csel    $acc3,$acc3,$t3,eq
667         csel    $ap,xzr,$ap,eq
668
669         lsr     $acc0,$acc0,#1          // ret >>= 1
670         orr     $acc0,$acc0,$acc1,lsl#63
671         lsr     $acc1,$acc1,#1
672         orr     $acc1,$acc1,$acc2,lsl#63
673         lsr     $acc2,$acc2,#1
674         orr     $acc2,$acc2,$acc3,lsl#63
675         lsr     $acc3,$acc3,#1
676         stp     $acc0,$acc1,[$rp]
677         orr     $acc3,$acc3,$ap,lsl#63
678         stp     $acc2,$acc3,[$rp,#16]
679
680         ret
681 .size   __ecp_nistz256_div_by_2,.-__ecp_nistz256_div_by_2
682 ___
683 ########################################################################
684 # following subroutines are "literal" implementation of those found in
685 # ecp_nistz256.c
686 #
687 ########################################################################
688 # void ecp_nistz256_point_double(P256_POINT *out,const P256_POINT *inp);
689 #
690 {
691 my ($S,$M,$Zsqr,$tmp0)=map(32*$_,(0..3));
692 # above map() describes stack layout with 4 temporary
693 # 256-bit vectors on top.
694 my ($rp_real,$ap_real) = map("x$_",(21,22));
695
696 $code.=<<___;
697 .globl  ecp_nistz256_point_double
698 .type   ecp_nistz256_point_double,%function
699 .align  5
700 ecp_nistz256_point_double:
701         stp     x29,x30,[sp,#-80]!
702         add     x29,sp,#0
703         stp     x19,x20,[sp,#16]
704         stp     x21,x22,[sp,#32]
705         sub     sp,sp,#32*4
706
707 .Ldouble_shortcut:
708         ldp     $acc0,$acc1,[$ap,#32]
709          mov    $rp_real,$rp
710         ldp     $acc2,$acc3,[$ap,#48]
711          mov    $ap_real,$ap
712          ldr    $poly1,.Lpoly+8
713         mov     $t0,$acc0
714          ldr    $poly3,.Lpoly+24
715         mov     $t1,$acc1
716          ldp    $a0,$a1,[$ap_real,#64]  // forward load for p256_sqr_mont
717         mov     $t2,$acc2
718         mov     $t3,$acc3
719          ldp    $a2,$a3,[$ap_real,#64+16]
720         add     $rp,sp,#$S
721         bl      __ecp_nistz256_add      // p256_mul_by_2(S, in_y);
722
723         add     $rp,sp,#$Zsqr
724         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Zsqr, in_z);
725
726         ldp     $t0,$t1,[$ap_real]
727         ldp     $t2,$t3,[$ap_real,#16]
728         mov     $a0,$acc0               // put Zsqr aside for p256_sub
729         mov     $a1,$acc1
730         mov     $a2,$acc2
731         mov     $a3,$acc3
732         add     $rp,sp,#$M
733         bl      __ecp_nistz256_add      // p256_add(M, Zsqr, in_x);
734
735         add     $bp,$ap_real,#0
736         mov     $acc0,$a0               // restore Zsqr
737         mov     $acc1,$a1
738          ldp    $a0,$a1,[sp,#$S]        // forward load for p256_sqr_mont
739         mov     $acc2,$a2
740         mov     $acc3,$a3
741          ldp    $a2,$a3,[sp,#$S+16]
742         add     $rp,sp,#$Zsqr
743         bl      __ecp_nistz256_sub_morf // p256_sub(Zsqr, in_x, Zsqr);
744
745         add     $rp,sp,#$S
746         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(S, S);
747
748         ldr     $bi,[$ap_real,#32]
749         ldp     $a0,$a1,[$ap_real,#64]
750         ldp     $a2,$a3,[$ap_real,#64+16]
751         add     $bp,$ap_real,#32
752         add     $rp,sp,#$tmp0
753         bl      __ecp_nistz256_mul_mont // p256_mul_mont(tmp0, in_z, in_y);
754
755         mov     $t0,$acc0
756         mov     $t1,$acc1
757          ldp    $a0,$a1,[sp,#$S]        // forward load for p256_sqr_mont
758         mov     $t2,$acc2
759         mov     $t3,$acc3
760          ldp    $a2,$a3,[sp,#$S+16]
761         add     $rp,$rp_real,#64
762         bl      __ecp_nistz256_add      // p256_mul_by_2(res_z, tmp0);
763
764         add     $rp,sp,#$tmp0
765         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(tmp0, S);
766
767          ldr    $bi,[sp,#$Zsqr]         // forward load for p256_mul_mont
768          ldp    $a0,$a1,[sp,#$M]
769          ldp    $a2,$a3,[sp,#$M+16]
770         add     $rp,$rp_real,#32
771         bl      __ecp_nistz256_div_by_2 // p256_div_by_2(res_y, tmp0);
772
773         add     $bp,sp,#$Zsqr
774         add     $rp,sp,#$M
775         bl      __ecp_nistz256_mul_mont // p256_mul_mont(M, M, Zsqr);
776
777         mov     $t0,$acc0               // duplicate M
778         mov     $t1,$acc1
779         mov     $t2,$acc2
780         mov     $t3,$acc3
781         mov     $a0,$acc0               // put M aside
782         mov     $a1,$acc1
783         mov     $a2,$acc2
784         mov     $a3,$acc3
785         add     $rp,sp,#$M
786         bl      __ecp_nistz256_add
787         mov     $t0,$a0                 // restore M
788         mov     $t1,$a1
789          ldr    $bi,[$ap_real]          // forward load for p256_mul_mont
790         mov     $t2,$a2
791          ldp    $a0,$a1,[sp,#$S]
792         mov     $t3,$a3
793          ldp    $a2,$a3,[sp,#$S+16]
794         bl      __ecp_nistz256_add      // p256_mul_by_3(M, M);
795
796         add     $bp,$ap_real,#0
797         add     $rp,sp,#$S
798         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S, S, in_x);
799
800         mov     $t0,$acc0
801         mov     $t1,$acc1
802          ldp    $a0,$a1,[sp,#$M]        // forward load for p256_sqr_mont
803         mov     $t2,$acc2
804         mov     $t3,$acc3
805          ldp    $a2,$a3,[sp,#$M+16]
806         add     $rp,sp,#$tmp0
807         bl      __ecp_nistz256_add      // p256_mul_by_2(tmp0, S);
808
809         add     $rp,$rp_real,#0
810         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(res_x, M);
811
812         add     $bp,sp,#$tmp0
813         bl      __ecp_nistz256_sub_from // p256_sub(res_x, res_x, tmp0);
814
815         add     $bp,sp,#$S
816         add     $rp,sp,#$S
817         bl      __ecp_nistz256_sub_morf // p256_sub(S, S, res_x);
818
819         ldr     $bi,[sp,#$M]
820         mov     $a0,$acc0               // copy S
821         mov     $a1,$acc1
822         mov     $a2,$acc2
823         mov     $a3,$acc3
824         add     $bp,sp,#$M
825         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S, S, M);
826
827         add     $bp,$rp_real,#32
828         add     $rp,$rp_real,#32
829         bl      __ecp_nistz256_sub_from // p256_sub(res_y, S, res_y);
830
831         add     sp,x29,#0               // destroy frame
832         ldp     x19,x20,[x29,#16]
833         ldp     x21,x22,[x29,#32]
834         ldp     x29,x30,[sp],#80
835         ret
836 .size   ecp_nistz256_point_double,.-ecp_nistz256_point_double
837 ___
838 }
839
840 ########################################################################
841 # void ecp_nistz256_point_add(P256_POINT *out,const P256_POINT *in1,
842 #                             const P256_POINT *in2);
843 {
844 my ($res_x,$res_y,$res_z,
845     $H,$Hsqr,$R,$Rsqr,$Hcub,
846     $U1,$U2,$S1,$S2)=map(32*$_,(0..11));
847 my ($Z1sqr, $Z2sqr) = ($Hsqr, $Rsqr);
848 # above map() describes stack layout with 12 temporary
849 # 256-bit vectors on top.
850 my ($rp_real,$ap_real,$bp_real,$in1infty,$in2infty,$temp)=map("x$_",(21..26));
851
852 $code.=<<___;
853 .globl  ecp_nistz256_point_add
854 .type   ecp_nistz256_point_add,%function
855 .align  5
856 ecp_nistz256_point_add:
857         stp     x29,x30,[sp,#-80]!
858         add     x29,sp,#0
859         stp     x19,x20,[sp,#16]
860         stp     x21,x22,[sp,#32]
861         stp     x23,x24,[sp,#48]
862         stp     x25,x26,[sp,#64]
863         sub     sp,sp,#32*12
864
865         ldp     $a0,$a1,[$bp]
866         ldp     $a2,$a3,[$bp,#16]
867         ldp     $t0,$t1,[$bp,#32]
868         ldp     $t2,$t3,[$bp,#48]
869          mov    $rp_real,$rp
870          mov    $ap_real,$ap
871          mov    $bp_real,$bp
872         orr     $a0,$a0,$a1
873         orr     $a2,$a2,$a3
874          ldp    $acc0,$acc1,[$ap]
875         orr     $t0,$t0,$t1
876         orr     $t2,$t2,$t3
877          ldp    $acc2,$acc3,[$ap,#16]
878         orr     $a0,$a0,$a2
879         orr     $t2,$t0,$t2
880          ldp    $t0,$t1,[$ap,#32]
881         orr     $in2infty,$a0,$t2
882         cmp     $in2infty,#0
883          ldp    $t2,$t3,[$ap,#48]
884         csetm   $in2infty,ne            // !in2infty
885
886          ldp    $a0,$a1,[$bp_real,#64]  // forward load for p256_sqr_mont
887         orr     $acc0,$acc0,$acc1
888         orr     $acc2,$acc2,$acc3
889          ldp    $a2,$a3,[$bp_real,#64+16]
890         orr     $t0,$t0,$t1
891         orr     $t2,$t2,$t3
892         orr     $acc0,$acc0,$acc2
893         orr     $t0,$t0,$t2
894         orr     $in1infty,$acc0,$t0
895         cmp     $in1infty,#0
896          ldr    $poly1,.Lpoly+8
897          ldr    $poly3,.Lpoly+24
898         csetm   $in1infty,ne            // !in1infty
899
900         add     $rp,sp,#$Z2sqr
901         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Z2sqr, in2_z);
902
903         ldp     $a0,$a1,[$ap_real,#64]
904         ldp     $a2,$a3,[$ap_real,#64+16]
905         add     $rp,sp,#$Z1sqr
906         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Z1sqr, in1_z);
907
908         ldr     $bi,[$bp_real,#64]
909         ldp     $a0,$a1,[sp,#$Z2sqr]
910         ldp     $a2,$a3,[sp,#$Z2sqr+16]
911         add     $bp,$bp_real,#64
912         add     $rp,sp,#$S1
913         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S1, Z2sqr, in2_z);
914
915         ldr     $bi,[$ap_real,#64]
916         ldp     $a0,$a1,[sp,#$Z1sqr]
917         ldp     $a2,$a3,[sp,#$Z1sqr+16]
918         add     $bp,$ap_real,#64
919         add     $rp,sp,#$S2
920         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, Z1sqr, in1_z);
921
922         ldr     $bi,[$ap_real,#32]
923         ldp     $a0,$a1,[sp,#$S1]
924         ldp     $a2,$a3,[sp,#$S1+16]
925         add     $bp,$ap_real,#32
926         add     $rp,sp,#$S1
927         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S1, S1, in1_y);
928
929         ldr     $bi,[$bp_real,#32]
930         ldp     $a0,$a1,[sp,#$S2]
931         ldp     $a2,$a3,[sp,#$S2+16]
932         add     $bp,$bp_real,#32
933         add     $rp,sp,#$S2
934         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, S2, in2_y);
935
936         add     $bp,sp,#$S1
937          ldr    $bi,[sp,#$Z2sqr]        // forward load for p256_mul_mont
938          ldp    $a0,$a1,[$ap_real]
939          ldp    $a2,$a3,[$ap_real,#16]
940         add     $rp,sp,#$R
941         bl      __ecp_nistz256_sub_from // p256_sub(R, S2, S1);
942
943         orr     $acc0,$acc0,$acc1       // see if result is zero
944         orr     $acc2,$acc2,$acc3
945         orr     $temp,$acc0,$acc2
946
947         add     $bp,sp,#$Z2sqr
948         add     $rp,sp,#$U1
949         bl      __ecp_nistz256_mul_mont // p256_mul_mont(U1, in1_x, Z2sqr);
950
951         ldr     $bi,[sp,#$Z1sqr]
952         ldp     $a0,$a1,[$bp_real]
953         ldp     $a2,$a3,[$bp_real,#16]
954         add     $bp,sp,#$Z1sqr
955         add     $rp,sp,#$U2
956         bl      __ecp_nistz256_mul_mont // p256_mul_mont(U2, in2_x, Z1sqr);
957
958         add     $bp,sp,#$U1
959          ldp    $a0,$a1,[sp,#$R]        // forward load for p256_sqr_mont
960          ldp    $a2,$a3,[sp,#$R+16]
961         add     $rp,sp,#$H
962         bl      __ecp_nistz256_sub_from // p256_sub(H, U2, U1);
963
964         orr     $acc0,$acc0,$acc1       // see if result is zero
965         orr     $acc2,$acc2,$acc3
966         orr     $acc0,$acc0,$acc2
967         tst     $acc0,$acc0
968         b.ne    .Ladd_proceed           // is_equal(U1,U2)?
969
970         tst     $in1infty,$in2infty
971         b.eq    .Ladd_proceed           // (in1infty || in2infty)?
972
973         tst     $temp,$temp
974         b.eq    .Ladd_double            // is_equal(S1,S2)?
975
976         eor     $a0,$a0,$a0
977         eor     $a1,$a1,$a1
978         stp     $a0,$a1,[$rp_real]
979         stp     $a0,$a1,[$rp_real,#16]
980         stp     $a0,$a1,[$rp_real,#32]
981         stp     $a0,$a1,[$rp_real,#48]
982         stp     $a0,$a1,[$rp_real,#64]
983         stp     $a0,$a1,[$rp_real,#80]
984         b       .Ladd_done
985
986 .align  4
987 .Ladd_double:
988         mov     $ap,$ap_real
989         mov     $rp,$rp_real
990         ldp     x23,x24,[x29,#48]
991         ldp     x25,x26,[x29,#64]
992         add     sp,sp,#32*(12-4)        // difference in stack frames
993         b       .Ldouble_shortcut
994
995 .align  4
996 .Ladd_proceed:
997         add     $rp,sp,#$Rsqr
998         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Rsqr, R);
999
1000         ldr     $bi,[$ap_real,#64]
1001         ldp     $a0,$a1,[sp,#$H]
1002         ldp     $a2,$a3,[sp,#$H+16]
1003         add     $bp,$ap_real,#64
1004         add     $rp,sp,#$res_z
1005         bl      __ecp_nistz256_mul_mont // p256_mul_mont(res_z, H, in1_z);
1006
1007         ldp     $a0,$a1,[sp,#$H]
1008         ldp     $a2,$a3,[sp,#$H+16]
1009         add     $rp,sp,#$Hsqr
1010         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Hsqr, H);
1011
1012         ldr     $bi,[$bp_real,#64]
1013         ldp     $a0,$a1,[sp,#$res_z]
1014         ldp     $a2,$a3,[sp,#$res_z+16]
1015         add     $bp,$bp_real,#64
1016         add     $rp,sp,#$res_z
1017         bl      __ecp_nistz256_mul_mont // p256_mul_mont(res_z, res_z, in2_z);
1018
1019         ldr     $bi,[sp,#$H]
1020         ldp     $a0,$a1,[sp,#$Hsqr]
1021         ldp     $a2,$a3,[sp,#$Hsqr+16]
1022         add     $bp,sp,#$H
1023         add     $rp,sp,#$Hcub
1024         bl      __ecp_nistz256_mul_mont // p256_mul_mont(Hcub, Hsqr, H);
1025
1026         ldr     $bi,[sp,#$Hsqr]
1027         ldp     $a0,$a1,[sp,#$U1]
1028         ldp     $a2,$a3,[sp,#$U1+16]
1029         add     $bp,sp,#$Hsqr
1030         add     $rp,sp,#$U2
1031         bl      __ecp_nistz256_mul_mont // p256_mul_mont(U2, U1, Hsqr);
1032
1033         mov     $t0,$acc0
1034         mov     $t1,$acc1
1035         mov     $t2,$acc2
1036         mov     $t3,$acc3
1037         add     $rp,sp,#$Hsqr
1038         bl      __ecp_nistz256_add      // p256_mul_by_2(Hsqr, U2);
1039
1040         add     $bp,sp,#$Rsqr
1041         add     $rp,sp,#$res_x
1042         bl      __ecp_nistz256_sub_morf // p256_sub(res_x, Rsqr, Hsqr);
1043
1044         add     $bp,sp,#$Hcub
1045         bl      __ecp_nistz256_sub_from //  p256_sub(res_x, res_x, Hcub);
1046
1047         add     $bp,sp,#$U2
1048          ldr    $bi,[sp,#$Hcub]         // forward load for p256_mul_mont
1049          ldp    $a0,$a1,[sp,#$S1]
1050          ldp    $a2,$a3,[sp,#$S1+16]
1051         add     $rp,sp,#$res_y
1052         bl      __ecp_nistz256_sub_morf // p256_sub(res_y, U2, res_x);
1053
1054         add     $bp,sp,#$Hcub
1055         add     $rp,sp,#$S2
1056         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, S1, Hcub);
1057
1058         ldr     $bi,[sp,#$R]
1059         ldp     $a0,$a1,[sp,#$res_y]
1060         ldp     $a2,$a3,[sp,#$res_y+16]
1061         add     $bp,sp,#$R
1062         add     $rp,sp,#$res_y
1063         bl      __ecp_nistz256_mul_mont // p256_mul_mont(res_y, res_y, R);
1064
1065         add     $bp,sp,#$S2
1066         bl      __ecp_nistz256_sub_from // p256_sub(res_y, res_y, S2);
1067
1068         ldp     $a0,$a1,[sp,#$res_x]            // res
1069         ldp     $a2,$a3,[sp,#$res_x+16]
1070         ldp     $t0,$t1,[$bp_real]              // in2
1071         ldp     $t2,$t3,[$bp_real,#16]
1072 ___
1073 for($i=0;$i<64;$i+=32) {                # conditional moves
1074 $code.=<<___;
1075         ldp     $acc0,$acc1,[$ap_real,#$i]      // in1
1076         cmp     $in1infty,#0                    // !$in1intfy, remember?
1077         ldp     $acc2,$acc3,[$ap_real,#$i+16]
1078         csel    $t0,$a0,$t0,ne
1079         csel    $t1,$a1,$t1,ne
1080         ldp     $a0,$a1,[sp,#$res_x+$i+32]      // res
1081         csel    $t2,$a2,$t2,ne
1082         csel    $t3,$a3,$t3,ne
1083         cmp     $in2infty,#0                    // !$in2intfy, remember?
1084         ldp     $a2,$a3,[sp,#$res_x+$i+48]
1085         csel    $acc0,$t0,$acc0,ne
1086         csel    $acc1,$t1,$acc1,ne
1087         ldp     $t0,$t1,[$bp_real,#$i+32]       // in2
1088         csel    $acc2,$t2,$acc2,ne
1089         csel    $acc3,$t3,$acc3,ne
1090         ldp     $t2,$t3,[$bp_real,#$i+48]
1091         stp     $acc0,$acc1,[$rp_real,#$i]
1092         stp     $acc2,$acc3,[$rp_real,#$i+16]
1093 ___
1094 }
1095 $code.=<<___;
1096         ldp     $acc0,$acc1,[$ap_real,#$i]      // in1
1097         cmp     $in1infty,#0                    // !$in1intfy, remember?
1098         ldp     $acc2,$acc3,[$ap_real,#$i+16]
1099         csel    $t0,$a0,$t0,ne
1100         csel    $t1,$a1,$t1,ne
1101         csel    $t2,$a2,$t2,ne
1102         csel    $t3,$a3,$t3,ne
1103         cmp     $in2infty,#0                    // !$in2intfy, remember?
1104         csel    $acc0,$t0,$acc0,ne
1105         csel    $acc1,$t1,$acc1,ne
1106         csel    $acc2,$t2,$acc2,ne
1107         csel    $acc3,$t3,$acc3,ne
1108         stp     $acc0,$acc1,[$rp_real,#$i]
1109         stp     $acc2,$acc3,[$rp_real,#$i+16]
1110
1111 .Ladd_done:
1112         add     sp,x29,#0       // destroy frame
1113         ldp     x19,x20,[x29,#16]
1114         ldp     x21,x22,[x29,#32]
1115         ldp     x23,x24,[x29,#48]
1116         ldp     x25,x26,[x29,#64]
1117         ldp     x29,x30,[sp],#80
1118         ret
1119 .size   ecp_nistz256_point_add,.-ecp_nistz256_point_add
1120 ___
1121 }
1122
1123 ########################################################################
1124 # void ecp_nistz256_point_add_affine(P256_POINT *out,const P256_POINT *in1,
1125 #                                    const P256_POINT_AFFINE *in2);
1126 {
1127 my ($res_x,$res_y,$res_z,
1128     $U2,$S2,$H,$R,$Hsqr,$Hcub,$Rsqr)=map(32*$_,(0..9));
1129 my $Z1sqr = $S2;
1130 # above map() describes stack layout with 10 temporary
1131 # 256-bit vectors on top.
1132 my ($rp_real,$ap_real,$bp_real,$in1infty,$in2infty,$temp)=map("x$_",(21..26));
1133
1134 $code.=<<___;
1135 .globl  ecp_nistz256_point_add_affine
1136 .type   ecp_nistz256_point_add_affine,%function
1137 .align  5
1138 ecp_nistz256_point_add_affine:
1139         stp     x29,x30,[sp,#-80]!
1140         add     x29,sp,#0
1141         stp     x19,x20,[sp,#16]
1142         stp     x21,x22,[sp,#32]
1143         stp     x23,x24,[sp,#48]
1144         stp     x25,x26,[sp,#64]
1145         sub     sp,sp,#32*10
1146
1147         mov     $rp_real,$rp
1148         mov     $ap_real,$ap
1149         mov     $bp_real,$bp
1150         ldr     $poly1,.Lpoly+8
1151         ldr     $poly3,.Lpoly+24
1152
1153         ldp     $a0,$a1,[$ap]
1154         ldp     $a2,$a3,[$ap,#16]
1155         ldp     $t0,$t1,[$ap,#32]
1156         ldp     $t2,$t3,[$ap,#48]
1157         orr     $a0,$a0,$a1
1158         orr     $a2,$a2,$a3
1159         orr     $t0,$t0,$t1
1160         orr     $t2,$t2,$t3
1161         orr     $a0,$a0,$a2
1162         orr     $t0,$t0,$t2
1163         orr     $in1infty,$a0,$t0
1164         cmp     $in1infty,#0
1165         csetm   $in1infty,ne            // !in1infty
1166
1167         ldp     $a0,$a1,[$bp]
1168         ldp     $a2,$a3,[$bp,#16]
1169         ldp     $t0,$t1,[$bp,#32]
1170         ldp     $t2,$t3,[$bp,#48]
1171         orr     $a0,$a0,$a1
1172         orr     $a2,$a2,$a3
1173         orr     $t0,$t0,$t1
1174         orr     $t2,$t2,$t3
1175         orr     $a0,$a0,$a2
1176         orr     $t0,$t0,$t2
1177         orr     $in2infty,$a0,$t0
1178         cmp     $in2infty,#0
1179         csetm   $in2infty,ne            // !in2infty
1180
1181         ldp     $a0,$a1,[$ap_real,#64]
1182         ldp     $a2,$a3,[$ap_real,#64+16]
1183         add     $rp,sp,#$Z1sqr
1184         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Z1sqr, in1_z);
1185
1186         mov     $a0,$acc0
1187         mov     $a1,$acc1
1188         mov     $a2,$acc2
1189         mov     $a3,$acc3
1190         ldr     $bi,[$bp_real]
1191         add     $bp,$bp_real,#0
1192         add     $rp,sp,#$U2
1193         bl      __ecp_nistz256_mul_mont // p256_mul_mont(U2, Z1sqr, in2_x);
1194
1195         add     $bp,$ap_real,#0
1196          ldr    $bi,[$ap_real,#64]      // forward load for p256_mul_mont
1197          ldp    $a0,$a1,[sp,#$Z1sqr]
1198          ldp    $a2,$a3,[sp,#$Z1sqr+16]
1199         add     $rp,sp,#$H
1200         bl      __ecp_nistz256_sub_from // p256_sub(H, U2, in1_x);
1201
1202         add     $bp,$ap_real,#64
1203         add     $rp,sp,#$S2
1204         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, Z1sqr, in1_z);
1205
1206         ldr     $bi,[$ap_real,#64]
1207         ldp     $a0,$a1,[sp,#$H]
1208         ldp     $a2,$a3,[sp,#$H+16]
1209         add     $bp,$ap_real,#64
1210         add     $rp,sp,#$res_z
1211         bl      __ecp_nistz256_mul_mont // p256_mul_mont(res_z, H, in1_z);
1212
1213         ldr     $bi,[$bp_real,#32]
1214         ldp     $a0,$a1,[sp,#$S2]
1215         ldp     $a2,$a3,[sp,#$S2+16]
1216         add     $bp,$bp_real,#32
1217         add     $rp,sp,#$S2
1218         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, S2, in2_y);
1219
1220         add     $bp,$ap_real,#32
1221          ldp    $a0,$a1,[sp,#$H]        // forward load for p256_sqr_mont
1222          ldp    $a2,$a3,[sp,#$H+16]
1223         add     $rp,sp,#$R
1224         bl      __ecp_nistz256_sub_from // p256_sub(R, S2, in1_y);
1225
1226         add     $rp,sp,#$Hsqr
1227         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Hsqr, H);
1228
1229         ldp     $a0,$a1,[sp,#$R]
1230         ldp     $a2,$a3,[sp,#$R+16]
1231         add     $rp,sp,#$Rsqr
1232         bl      __ecp_nistz256_sqr_mont // p256_sqr_mont(Rsqr, R);
1233
1234         ldr     $bi,[sp,#$H]
1235         ldp     $a0,$a1,[sp,#$Hsqr]
1236         ldp     $a2,$a3,[sp,#$Hsqr+16]
1237         add     $bp,sp,#$H
1238         add     $rp,sp,#$Hcub
1239         bl      __ecp_nistz256_mul_mont // p256_mul_mont(Hcub, Hsqr, H);
1240
1241         ldr     $bi,[$ap_real]
1242         ldp     $a0,$a1,[sp,#$Hsqr]
1243         ldp     $a2,$a3,[sp,#$Hsqr+16]
1244         add     $bp,$ap_real,#0
1245         add     $rp,sp,#$U2
1246         bl      __ecp_nistz256_mul_mont // p256_mul_mont(U2, in1_x, Hsqr);
1247
1248         mov     $t0,$acc0
1249         mov     $t1,$acc1
1250         mov     $t2,$acc2
1251         mov     $t3,$acc3
1252         add     $rp,sp,#$Hsqr
1253         bl      __ecp_nistz256_add      // p256_mul_by_2(Hsqr, U2);
1254
1255         add     $bp,sp,#$Rsqr
1256         add     $rp,sp,#$res_x
1257         bl      __ecp_nistz256_sub_morf // p256_sub(res_x, Rsqr, Hsqr);
1258
1259         add     $bp,sp,#$Hcub
1260         bl      __ecp_nistz256_sub_from //  p256_sub(res_x, res_x, Hcub);
1261
1262         add     $bp,sp,#$U2
1263          ldr    $bi,[$ap_real,#32]      // forward load for p256_mul_mont
1264          ldp    $a0,$a1,[sp,#$Hcub]
1265          ldp    $a2,$a3,[sp,#$Hcub+16]
1266         add     $rp,sp,#$res_y
1267         bl      __ecp_nistz256_sub_morf // p256_sub(res_y, U2, res_x);
1268
1269         add     $bp,$ap_real,#32
1270         add     $rp,sp,#$S2
1271         bl      __ecp_nistz256_mul_mont // p256_mul_mont(S2, in1_y, Hcub);
1272
1273         ldr     $bi,[sp,#$R]
1274         ldp     $a0,$a1,[sp,#$res_y]
1275         ldp     $a2,$a3,[sp,#$res_y+16]
1276         add     $bp,sp,#$R
1277         add     $rp,sp,#$res_y
1278         bl      __ecp_nistz256_mul_mont // p256_mul_mont(res_y, res_y, R);
1279
1280         add     $bp,sp,#$S2
1281         bl      __ecp_nistz256_sub_from // p256_sub(res_y, res_y, S2);
1282
1283         ldp     $a0,$a1,[sp,#$res_x]            // res
1284         ldp     $a2,$a3,[sp,#$res_x+16]
1285         ldp     $t0,$t1,[$bp_real]              // in2
1286         ldp     $t2,$t3,[$bp_real,#16]
1287 ___
1288 for($i=0;$i<64;$i+=32) {                # conditional moves
1289 $code.=<<___;
1290         ldp     $acc0,$acc1,[$ap_real,#$i]      // in1
1291         cmp     $in1infty,#0                    // !$in1intfy, remember?
1292         ldp     $acc2,$acc3,[$ap_real,#$i+16]
1293         csel    $t0,$a0,$t0,ne
1294         csel    $t1,$a1,$t1,ne
1295         ldp     $a0,$a1,[sp,#$res_x+$i+32]      // res
1296         csel    $t2,$a2,$t2,ne
1297         csel    $t3,$a3,$t3,ne
1298         cmp     $in2infty,#0                    // !$in2intfy, remember?
1299         ldp     $a2,$a3,[sp,#$res_x+$i+48]
1300         csel    $acc0,$t0,$acc0,ne
1301         csel    $acc1,$t1,$acc1,ne
1302         ldp     $t0,$t1,[$bp_real,#$i+32]       // in2
1303         csel    $acc2,$t2,$acc2,ne
1304         csel    $acc3,$t3,$acc3,ne
1305         ldp     $t2,$t3,[$bp_real,#$i+48]
1306         stp     $acc0,$acc1,[$rp_real,#$i]
1307         stp     $acc2,$acc3,[$rp_real,#$i+16]
1308 ___
1309 $code.=<<___    if ($i == 0);
1310         adr     $bp_real,.Lone_mont-64
1311 ___
1312 }
1313 $code.=<<___;
1314         ldp     $acc0,$acc1,[$ap_real,#$i]      // in1
1315         cmp     $in1infty,#0                    // !$in1intfy, remember?
1316         ldp     $acc2,$acc3,[$ap_real,#$i+16]
1317         csel    $t0,$a0,$t0,ne
1318         csel    $t1,$a1,$t1,ne
1319         csel    $t2,$a2,$t2,ne
1320         csel    $t3,$a3,$t3,ne
1321         cmp     $in2infty,#0                    // !$in2intfy, remember?
1322         csel    $acc0,$t0,$acc0,ne
1323         csel    $acc1,$t1,$acc1,ne
1324         csel    $acc2,$t2,$acc2,ne
1325         csel    $acc3,$t3,$acc3,ne
1326         stp     $acc0,$acc1,[$rp_real,#$i]
1327         stp     $acc2,$acc3,[$rp_real,#$i+16]
1328
1329         add     sp,x29,#0               // destroy frame
1330         ldp     x19,x20,[x29,#16]
1331         ldp     x21,x22,[x29,#32]
1332         ldp     x23,x24,[x29,#48]
1333         ldp     x25,x26,[x29,#64]
1334         ldp     x29,x30,[sp],#80
1335         ret
1336 .size   ecp_nistz256_point_add_affine,.-ecp_nistz256_point_add_affine
1337 ___
1338 }       }
1339
1340 ########################################################################
1341 # scatter-gather subroutines
1342 {
1343 my ($out,$inp,$index,$mask)=map("x$_",(0..3));
1344 $code.=<<___;
1345 // void ecp_nistz256_scatter_w5(void *x0,const P256_POINT *x1,
1346 //                                       int x2);
1347 .globl  ecp_nistz256_scatter_w5
1348 .type   ecp_nistz256_scatter_w5,%function
1349 .align  4
1350 ecp_nistz256_scatter_w5:
1351         stp     x29,x30,[sp,#-16]!
1352         add     x29,sp,#0
1353
1354         add     $out,$out,$index,lsl#2
1355
1356         ldp     x4,x5,[$inp]            // X
1357         ldp     x6,x7,[$inp,#16]
1358         str     w4,[$out,#64*0-4]
1359         lsr     x4,x4,#32
1360         str     w5,[$out,#64*1-4]
1361         lsr     x5,x5,#32
1362         str     w6,[$out,#64*2-4]
1363         lsr     x6,x6,#32
1364         str     w7,[$out,#64*3-4]
1365         lsr     x7,x7,#32
1366         str     w4,[$out,#64*4-4]
1367         str     w5,[$out,#64*5-4]
1368         str     w6,[$out,#64*6-4]
1369         str     w7,[$out,#64*7-4]
1370         add     $out,$out,#64*8
1371
1372         ldp     x4,x5,[$inp,#32]        // Y
1373         ldp     x6,x7,[$inp,#48]
1374         str     w4,[$out,#64*0-4]
1375         lsr     x4,x4,#32
1376         str     w5,[$out,#64*1-4]
1377         lsr     x5,x5,#32
1378         str     w6,[$out,#64*2-4]
1379         lsr     x6,x6,#32
1380         str     w7,[$out,#64*3-4]
1381         lsr     x7,x7,#32
1382         str     w4,[$out,#64*4-4]
1383         str     w5,[$out,#64*5-4]
1384         str     w6,[$out,#64*6-4]
1385         str     w7,[$out,#64*7-4]
1386         add     $out,$out,#64*8
1387
1388         ldp     x4,x5,[$inp,#64]        // Z
1389         ldp     x6,x7,[$inp,#80]
1390         str     w4,[$out,#64*0-4]
1391         lsr     x4,x4,#32
1392         str     w5,[$out,#64*1-4]
1393         lsr     x5,x5,#32
1394         str     w6,[$out,#64*2-4]
1395         lsr     x6,x6,#32
1396         str     w7,[$out,#64*3-4]
1397         lsr     x7,x7,#32
1398         str     w4,[$out,#64*4-4]
1399         str     w5,[$out,#64*5-4]
1400         str     w6,[$out,#64*6-4]
1401         str     w7,[$out,#64*7-4]
1402
1403         ldr     x29,[sp],#16
1404         ret
1405 .size   ecp_nistz256_scatter_w5,.-ecp_nistz256_scatter_w5
1406
1407 // void ecp_nistz256_gather_w5(P256_POINT *x0,const void *x1,
1408 //                                            int x2);
1409 .globl  ecp_nistz256_gather_w5
1410 .type   ecp_nistz256_gather_w5,%function
1411 .align  4
1412 ecp_nistz256_gather_w5:
1413         stp     x29,x30,[sp,#-16]!
1414         add     x29,sp,#0
1415
1416         cmp     $index,xzr
1417         csetm   x3,ne
1418         add     $index,$index,x3
1419         add     $inp,$inp,$index,lsl#2
1420
1421         ldr     w4,[$inp,#64*0]
1422         ldr     w5,[$inp,#64*1]
1423         ldr     w6,[$inp,#64*2]
1424         ldr     w7,[$inp,#64*3]
1425         ldr     w8,[$inp,#64*4]
1426         ldr     w9,[$inp,#64*5]
1427         ldr     w10,[$inp,#64*6]
1428         ldr     w11,[$inp,#64*7]
1429         add     $inp,$inp,#64*8
1430         orr     x4,x4,x8,lsl#32
1431         orr     x5,x5,x9,lsl#32
1432         orr     x6,x6,x10,lsl#32
1433         orr     x7,x7,x11,lsl#32
1434         csel    x4,x4,xzr,ne
1435         csel    x5,x5,xzr,ne
1436         csel    x6,x6,xzr,ne
1437         csel    x7,x7,xzr,ne
1438         stp     x4,x5,[$out]            // X
1439         stp     x6,x7,[$out,#16]
1440
1441         ldr     w4,[$inp,#64*0]
1442         ldr     w5,[$inp,#64*1]
1443         ldr     w6,[$inp,#64*2]
1444         ldr     w7,[$inp,#64*3]
1445         ldr     w8,[$inp,#64*4]
1446         ldr     w9,[$inp,#64*5]
1447         ldr     w10,[$inp,#64*6]
1448         ldr     w11,[$inp,#64*7]
1449         add     $inp,$inp,#64*8
1450         orr     x4,x4,x8,lsl#32
1451         orr     x5,x5,x9,lsl#32
1452         orr     x6,x6,x10,lsl#32
1453         orr     x7,x7,x11,lsl#32
1454         csel    x4,x4,xzr,ne
1455         csel    x5,x5,xzr,ne
1456         csel    x6,x6,xzr,ne
1457         csel    x7,x7,xzr,ne
1458         stp     x4,x5,[$out,#32]        // Y
1459         stp     x6,x7,[$out,#48]
1460
1461         ldr     w4,[$inp,#64*0]
1462         ldr     w5,[$inp,#64*1]
1463         ldr     w6,[$inp,#64*2]
1464         ldr     w7,[$inp,#64*3]
1465         ldr     w8,[$inp,#64*4]
1466         ldr     w9,[$inp,#64*5]
1467         ldr     w10,[$inp,#64*6]
1468         ldr     w11,[$inp,#64*7]
1469         orr     x4,x4,x8,lsl#32
1470         orr     x5,x5,x9,lsl#32
1471         orr     x6,x6,x10,lsl#32
1472         orr     x7,x7,x11,lsl#32
1473         csel    x4,x4,xzr,ne
1474         csel    x5,x5,xzr,ne
1475         csel    x6,x6,xzr,ne
1476         csel    x7,x7,xzr,ne
1477         stp     x4,x5,[$out,#64]        // Z
1478         stp     x6,x7,[$out,#80]
1479
1480         ldr     x29,[sp],#16
1481         ret
1482 .size   ecp_nistz256_gather_w5,.-ecp_nistz256_gather_w5
1483
1484 // void ecp_nistz256_scatter_w7(void *x0,const P256_POINT_AFFINE *x1,
1485 //                                       int x2);
1486 .globl  ecp_nistz256_scatter_w7
1487 .type   ecp_nistz256_scatter_w7,%function
1488 .align  4
1489 ecp_nistz256_scatter_w7:
1490         stp     x29,x30,[sp,#-16]!
1491         add     x29,sp,#0
1492
1493         add     $out,$out,$index
1494         mov     $index,#64/8
1495 .Loop_scatter_w7:
1496         ldr     x3,[$inp],#8
1497         subs    $index,$index,#1
1498         prfm    pstl1strm,[$out,#4096+64*0]
1499         prfm    pstl1strm,[$out,#4096+64*1]
1500         prfm    pstl1strm,[$out,#4096+64*2]
1501         prfm    pstl1strm,[$out,#4096+64*3]
1502         prfm    pstl1strm,[$out,#4096+64*4]
1503         prfm    pstl1strm,[$out,#4096+64*5]
1504         prfm    pstl1strm,[$out,#4096+64*6]
1505         prfm    pstl1strm,[$out,#4096+64*7]
1506         strb    w3,[$out,#64*0-1]
1507         lsr     x3,x3,#8
1508         strb    w3,[$out,#64*1-1]
1509         lsr     x3,x3,#8
1510         strb    w3,[$out,#64*2-1]
1511         lsr     x3,x3,#8
1512         strb    w3,[$out,#64*3-1]
1513         lsr     x3,x3,#8
1514         strb    w3,[$out,#64*4-1]
1515         lsr     x3,x3,#8
1516         strb    w3,[$out,#64*5-1]
1517         lsr     x3,x3,#8
1518         strb    w3,[$out,#64*6-1]
1519         lsr     x3,x3,#8
1520         strb    w3,[$out,#64*7-1]
1521         add     $out,$out,#64*8
1522         b.ne    .Loop_scatter_w7
1523
1524         ldr     x29,[sp],#16
1525         ret
1526 .size   ecp_nistz256_scatter_w7,.-ecp_nistz256_scatter_w7
1527
1528 // void ecp_nistz256_gather_w7(P256_POINT_AFFINE *x0,const void *x1,
1529 //                                                   int x2);
1530 .globl  ecp_nistz256_gather_w7
1531 .type   ecp_nistz256_gather_w7,%function
1532 .align  4
1533 ecp_nistz256_gather_w7:
1534         stp     x29,x30,[sp,#-16]!
1535         add     x29,sp,#0
1536
1537         cmp     $index,xzr
1538         csetm   x3,ne
1539         add     $index,$index,x3
1540         add     $inp,$inp,$index
1541         mov     $index,#64/8
1542         nop
1543 .Loop_gather_w7:
1544         ldrb    w4,[$inp,#64*0]
1545         prfm    pldl1strm,[$inp,#4096+64*0]
1546         subs    $index,$index,#1
1547         ldrb    w5,[$inp,#64*1]
1548         prfm    pldl1strm,[$inp,#4096+64*1]
1549         ldrb    w6,[$inp,#64*2]
1550         prfm    pldl1strm,[$inp,#4096+64*2]
1551         ldrb    w7,[$inp,#64*3]
1552         prfm    pldl1strm,[$inp,#4096+64*3]
1553         ldrb    w8,[$inp,#64*4]
1554         prfm    pldl1strm,[$inp,#4096+64*4]
1555         ldrb    w9,[$inp,#64*5]
1556         prfm    pldl1strm,[$inp,#4096+64*5]
1557         ldrb    w10,[$inp,#64*6]
1558         prfm    pldl1strm,[$inp,#4096+64*6]
1559         ldrb    w11,[$inp,#64*7]
1560         prfm    pldl1strm,[$inp,#4096+64*7]
1561         add     $inp,$inp,#64*8
1562         orr     x4,x4,x5,lsl#8
1563         orr     x6,x6,x7,lsl#8
1564         orr     x8,x8,x9,lsl#8
1565         orr     x4,x4,x6,lsl#16
1566         orr     x10,x10,x11,lsl#8
1567         orr     x4,x4,x8,lsl#32
1568         orr     x4,x4,x10,lsl#48
1569         and     x4,x4,x3
1570         str     x4,[$out],#8
1571         b.ne    .Loop_gather_w7
1572
1573         ldr     x29,[sp],#16
1574         ret
1575 .size   ecp_nistz256_gather_w7,.-ecp_nistz256_gather_w7
1576 ___
1577 }
1578
1579 foreach (split("\n",$code)) {
1580         s/\`([^\`]*)\`/eval $1/ge;
1581
1582         print $_,"\n";
1583 }
1584 close STDOUT;   # enforce flush